1. 程式人生 > 實用技巧 >b_51_字串中的最大值(next陣列+倒序遞推)

b_51_字串中的最大值(next陣列+倒序遞推)

一個字串的字首是指包含該字元第一個字母的連續子串,例如:abcd的所有字首為a, ab, abc, abcd。
給出一個字串S,求其所有字首中,字元長度與出現次數的乘積的最大值。
例如:S = "abababa" 所有的字首如下:

"a", 長度與出現次數的乘積 1 * 4 = 4,
"ab",長度與出現次數的乘積 2 * 3 = 6,
"aba", 長度與出現次數的乘積 3 * 3 = 9,
"abab", 長度與出現次數的乘積 4 * 2 = 8,
"ababa", 長度與出現次數的乘積 5 * 2 = 10,
"ababab", 長度與出現次數的乘積 6 * 1 = 6,
"abababa", 長度與出現次數的乘積 7 * 1 = 7.

其中"ababa"出現了2次,二者的乘積為10,是所有字首中最大的。

思路
利用nx[j]表示,子串t[0:j]中最長公共前後綴的長度,假如當前位置j所對應的子串為t[0:j]
t[0:j]中有後綴t[k:j],則nx[j]=len時,則證明字首t[0:len]和t[k:j]是相等的,也就是出現了兩次
依次類推(根據next陣列使指標倒退的含義,我們應該倒著做)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+5;
ll n,ans,nx[N],f[N];
void get_nx(char s[]) {
    for (int i=2,j=0; i<=n; i++) {
        while (j && s[i]!=s[j+1]) j=nx[j];
        if (s[i]==s[j+1]) j++;
        nx[i]=j;
    }
}
int main() {
    std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    char s[N]; cin>>s+1;
    n=strlen(s+1);
    get_nx(s);
    for (int i=1; i<=n; i++) f[i]=1;
    for (int i=n; i; i--) f[nx[i]]+=f[i];
    for (int i=1; i<=n; i++) ans=max(ans,i*f[i]);
    cout<<ans;
    return 0;
}