Самая длинная максимальная повторяющаяся подстрока

Подстрока может иметь длину 1,2,3... Вопрос, который я пытался решить, заключался в поиске подстроки, которая встречается максимальное количество раз. Таким образом, в основном это сводилось к поиску персонажа с максимальной частотой. Однако я обнаружил, что могу найти самую длинную повторяющуюся подстроку, используя дерево суффиксов за O (n). Но дерево суффиксов возвращает подстроку, сохраняя длину в качестве приоритета. Я хотел найти подстроку, которая встречается наибольшее количество раз, и из этих подстрок я хочу найти самую длинную. Например:

In the following string: ABCZLMNABCZLMNABC
A suffix tree will return ABCZLMN as the longest repeating substring.
However, what I am looking for is ABC;  as it is the longest out of all the ones having frequency = 3. 

Я попытался решить эту проблему, сгенерировав подстроку между двумя индексами i и j. После этого нахождение вхождений этих подстрок в каждом случае с использованием Z-алгоритма, работающего за O(n). Однако общая сложность была O (n ^ 3)

Мой код O(n^3)

map<ll,vector<string>> m;
    string s; cin >> s;
    for(ll i=0;i<s.length();i++){
        string c;
        for(ll len=0; i+len<s.length();len++){
            c+=s[i+len];
            ll z[N];
            ll l=0,r=0;
            string kk;
            for(ll p=0;p<c.length();p++){
                kk+=c[p];
            }
            kk+="#";
            for(ll p=0;p<s.length();p++){
                kk+=s[p];
            }
            for(ll k=1;k<kk.length();k++){
                if(k>r){
                    l=r=k;
                    while(r<c.length()&&kk[r-l]==kk[r])r++;
                    z[k]=r-l;
                    r--;
                }
                else{
                    ll m=k-l;
                    if(z[m]<r-k+l)z[k]=z[m];
                    else{
                        l=k;
                        while(r<c.length()&&kk[r-l]==kk[r])r++;
                        z[k]=r-l;
                        r--;
                    }
                }
            }
            ll occ=0;
            for(ll n=0;n<kk.length();n++){
                if(z[n]==c.length())occ++;
            }
            m[occ].push_back(c);
        }
    }

Я не могу найти подходящего решения, чтобы сделать его эффективным. Пожалуйста, помогите. Спасибо.


person Anand Zutshi    schedule 14.07.2016    source источник
comment
Хе... домашние задания? ТАК не делай это для меня сайт. Попробуйте написать код и вернуться, если что-то пойдет не так.   -  person folibis    schedule 14.07.2016
comment
Хорошо, но покажи хотя бы свой код.   -  person folibis    schedule 14.07.2016
comment
Я попытался решить эту проблему, сгенерировав подстроку между двумя индексами i и j. После этого нахождение вхождений этих подстрок в каждом случае с использованием Z-алгоритма, работающего за O(n). Однако общая сложность была O (n ^ 3).   -  person Anand Zutshi    schedule 14.07.2016
comment
Является ли один символ подстрокой? Если это так, то подстрока, которая повторяется наибольшее количество раз, должна повторяться столько раз, сколько самый распространенный символ. Правильный?   -  person samgak    schedule 14.07.2016
comment
@samgak Да, вы правы, так и будет, однако я ищу самую длинную повторяющуюся подстроку из всех. Обратитесь к моему примеру в посте.   -  person Anand Zutshi    schedule 14.07.2016
comment
Хорошо, но подумайте о последствиях этого: вам просто нужно подсчитать частоту каждого символа и найти символы с одинаковой максимальной частотой. Один из них должен быть первым символом вашей самой длинной повторяющейся подстроки. Так что просто тестируйте каждый. Это O(n * k), k — количество возможных символов.   -  person samgak    schedule 14.07.2016


Ответы (1)


Один символ считается подстрокой, поэтому максимальная повторяющаяся подстрока должна встречаться с частотой, равной частоте наиболее часто встречающегося символа в строке.

Одним из следствий этого является то, что каждый символ в максимально повторяющейся подстроке может встречаться в строке только один раз, потому что, если он встречается более одного раза, то этот символ сам по себе станет максимально повторяющейся строкой. Например, подстрока «папа» встречается в строке «dadxdadydadzdadydad» 5 раз, а подстрока «d» — 10 раз.

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

Следовательно, максимальная повторяющаяся подстрока должна состоять из подмножества (или всех) одинаково часто встречающихся символов.

Мы можем легко определить, какие это символы, просто сделав один проход по строке и посчитав их. Мы также можем определить, какие символы появляются в каком порядке, отслеживая, какие символы появляются до и после каждого символа, сохраняя символ, если он каждый раз один и тот же, и ноль в противном случае. Например, в строке «abcxabcyabczabcyabc» символу «b» всегда предшествует «a», а за ним следует «c»:

string s; cin >> s;
int i, freq[256];
char prev[256], next[256];
for(i = 1; i < 256; i++)
    freq[i] = prev[i] = next[i] = 0;
int maxFreq = 0;
for(i = 0; i < s.length(); i++)
{
    char c = s[i];
    char p = (i == 0) ? 0 : s[i-1];
    char n = (i < s.length() - 1) ? s[i+1] : 0;
    if(freq[c] == 0) // first time to encounter this character
    {
        prev[c] = p;
        next[c] = n;
    }
    else // check if it is always preceded and followed by the same characters:
    {
        if(prev[c] != p)
            prev[c] = 0;
        if(next[c] != n)
            next[c] = 0;
    }
    // increment frequency and track the maximum:
    if(++freq[c] > maxFreq)
        maxFreq = freq[c];
}

if(maxFreq == 0)
    return 0;

Затем мы можем перебрать каждый символ и те, частота которых равна максимальной частоте, найти длину строки, которую мы можем сформировать, начиная с этого символа, следуя индексам символов next:

int maxLen = 0;
int startingChar = 0;
for(i = 1; i < 256; i++)
{
    // should have a frequency equal to the max and not be preceded
    // by the same character each time (or it is in the middle of the string)
    if((freq[i] == maxFreq) && (prev[i] == 0))
    {
        int len = 1, j = i;
        while(next[j] != 0)
        {
            len++;
            j = next[j];
        }
        if(len > maxLen)
        {
            maxLen = len;
            startingChar = i;
        }
    }
}

Как только мы нашли максимально повторяющуюся подстроку, распечатайте ее:

// print out the maximum length string:
int j = startingChar;
while(j != 0)
{
    cout << (char)j;
    j = next[j];
}
cout << endl;

Если вам не нравится перебирать эти массивы фиксированного размера или вам нужна поддержка символов UNICODE и т. д., вы можете использовать map из типа символа в структуру, содержащую частоту символов, а также предыдущие и следующие символы.

person samgak    schedule 14.07.2016