Если строка является итеративной подстрокой?

У меня есть строка S. Как найти строку, следующую за S = nT.

Примеры:
Функция должна возвращать true, если
1) S = "abab"
2) S = "abcdabcd"
3) S = "abcabcabc"
4) S = "zzxzzxzzx"

Но если S = ​​ "abcb" возвращает false.

Хотя, может быть, мы можем неоднократно называть KMP на подстроках S, а затем решать.

например: для "abab": вызовите KMP на "a". он возвращает 2 (два экземпляра). теперь 2 * len ( "a" )!= len (s)
вызовите KMP на "ab". он возвращает 2. теперь 2 * len ( "ab" ) == len (s), поэтому верните true

Можете ли вы предложить лучшие алгоритмы?

Ответ 1

Я могу думать об эвристике, но только вызов KMP на подстроке, если Len (исходная строка)/Len of (sub string) является положительным целым числом.

Также максимальная длина подстроки должна быть меньше N/2.

ИЗМЕНИТЬ

Используя эти эвристики, я написал следующий код python, потому что мой C ржавый в данный момент

oldstr='ABCDABCD'    

for i in xrange(0,len(oldstr)/2):
       newslice=oldstr[0:i+1]
         if newslice*(len(oldstr)/len(newslice)) == oldstr:
             print 'pattern found', newslice
             break

Ответ 2

Вам просто нужно заботиться о тестировании длины подстроки, которые равны полной длине строки , деленной на простое число. Причина в том, что если S содержит n копий T и n не является простым, то n = ab, и поэтому S фактически также содержит копии bT (где "bT" означает "T повторяется b раз" ). Это расширение anijhaw answer.

int primes[] = { 2, 3, 5, 7, 11, 13, 17 };  /* There are one or two more... ;) */
int nPrimes = sizeof primes / sizeof primes[0];

/* Passing in the string length instead of assuming ASCIIZ strings means we
 * don't have to modify the string in-place or allocate memory for new copies
 * to handle recursion. */
int is_iterative(char *s, int len) {
    int i, j;
    for (i = 0; i < nPrimes && primes[i] < len; ++i) {
        if (len % primes[i] == 0) {
            int sublen = len / primes[i];
            /* Is it possible that s consists of repeats of length sublen? */
            for (j = sublen; j < len; j += sublen) {
                if (memcmp(s, s + j, sublen)) {
                    break;
                }
            }

            if (j == len) {
                /* All length-sublen substrings are equal.  We could stop here
                 * (meaning e.g. "abababab" will report a correct, but
                 * non-minimal repeated substring of length 4), but let's
                 * recurse to see if an even shorter repeated substring
                 * can be found. */
                return is_iterative(s, sublen);
            }
        }
    }

    return len;     /* Could not be broken into shorter, repeated substrings */
}

Обратите внимание, что при повторном поиске, чтобы найти еще более короткие повторяющиеся подстроки, нам не нужно снова проверять всю строку, просто первое большее повторение - поскольку мы уже установили, что оставшиеся большие повторы являются, ну, повторениями первый.:)

Ответ 3

Я не вижу, что алгоритм KMP помогает в этом случае. Это не вопрос определения, с чего начать следующий матч. Кажется, что один из способов сократить время поиска - начать с самой длинной возможности (на половину длины) и работать вниз. Единственными длинами, которые нужно протестировать, являются длины, которые равномерно делятся на общую длину. Вот пример в Ruby. Я должен добавить, что я понимаю, что вопрос был помечен как C, но это был просто простой способ показать алгоритм, о котором я думал (и позволил мне проверить, что он сработал).

class String
def IsPattern( )
    len = self.length
    testlen = len / 2
    # the fastest is to start with two entries and work down
    while ( testlen > 0 )
        # if this is not an even divisor then it can't fit the pattern
        if ( len % testlen == 0 )
            # evenly divides, so it may match
            if ( self == self[0..testlen-1] * ( len / testlen ))
                return true
            end

        end
        testlen = testlen - 1
    end
    # must not have matched
    false
end
end

if __FILE__ == $0

   ARGV.each do |str|
       puts "%s, %s" % [str, str.IsPattern ? "true" : "false" ]
   end

end



[C:\test]ruby patterntest.rb a aa abab abcdabcd abcabcabc zzxzzxzzx abcd
a, false
aa, true
abab, true
abcdabcd, true
abcabcabc, true
zzxzzxzzx, true
abcd, false

Ответ 4

Я полагаю, вы могли бы попробовать следующий алгоритм:

Позволяет L быть возможной длиной подстроки, которая генерирует исходное слово. Для L от 1 до strlen(s)/2 проверьте, получает ли первый символ во всех позициях L*i для i от 1 до strlen(s)/L. Если это так, то это может быть возможным решением, и вы должны проверить его с помощью memcmp, если не попробовать следующий L. Конечно, вы можете пропустить некоторые значения L, которые не делятся на strlen(s).

Ответ 5

Попробуйте следующее:

    char s[] = "abcabcabcabc";
int nStringLength = strlen (s);
int nMaxCheckLength = nStringLength / 2;
int nThisOffset;
int nNumberOfSubStrings;
char cMustMatch;
char cCompare;
BOOL bThisSubStringLengthRepeats;
// Check all sub string lengths up to half the total length
for (int nSubStringLength = 1;  nSubStringLength <= nMaxCheckLength;  nSubStringLength++)
{
    // How many substrings will there be?
    nNumberOfSubStrings = nStringLength / nSubStringLength;

    // Only check substrings that fit exactly
    if (nSubStringLength * nNumberOfSubStrings == nStringLength)
    {
        // Assume it going to be ok
        bThisSubStringLengthRepeats = TRUE;

        // check each character in substring
        for (nThisOffset = 0;  nThisOffset < nSubStringLength;  nThisOffset++)
        {
            // What must it be?
            cMustMatch = s [nThisOffset];

            // check each substring char in that position
            for (int nSubString = 1;  nSubString < nNumberOfSubStrings;  nSubString++)
            {
                cCompare = s [(nSubString * nSubStringLength) + nThisOffset];
                // Don't bother checking more if this doesn't match
                if (cCompare != cMustMatch)
                {
                    bThisSubStringLengthRepeats = FALSE;
                    break;
                }
            }

            // Stop checking this substring
            if (!bThisSubStringLengthRepeats)
            {
                break;
            }
        }

        // We have found a match!
        if (bThisSubStringLengthRepeats)
        {
            return TRUE;
        }
    }
}

// We went through the whole lot, but no matches found
return FALSE;

Ответ 6

Это код Java, но вы должны понять:

        String str = "ababcababc";
    int repPos = 0;
    int repLen = 0;
    for( int i = 0; i < str.length(); i++ ) {
        if( repLen == 0 ) {
            repLen = 1;
        } else {
            char c = str.charAt( i );
            if( c == str.charAt( repPos ) ) {
                repPos = ++repPos % repLen;
            } else {
                repLen = i+1;
            }
        }
    }

Это вернет длину кратчайшего повторяющегося фрагмента или длины строки, если повторения не будет.

Ответ 7

Вы можете построить массив суффикса строки, отсортировать его.
Теперь найдите серию суффиксов с удвоением и когда вы достигли того, что размер всей строки (S) первый в серии даст вам T.

Например:

abcd <-- T
abcdabcd <-- S
bcd
bcdabcd
cd
cdabcd
d
dabcd

x
xzzx
xzzxzzx
zx
zxzzx
zxzzxzzx
zzx <-- T
zzxzzx
zzxzzxzzx <-- S

a
apa
apapa
apapapa
pa <-- T
papa
papapa <-- Another T, not detected by this algo
papapapa <-- S