Description

\(num[i]\) 表示字符串 \(S\) 的前 \(i\) 个字符构成的子串,既是它的后缀同时又是它的前缀,并且该后缀与该前缀不重叠,这种字符串的数量。给定一个字符串,求出它的 \(num[]\)

Solution

\(ans[i]\) 表示字符串 \(S\) 的前 \(i\) 个字符构成的子串,既是它的后缀同时又是它的前缀,这种字符串的数量,显然 \(ans[]\) 可以在标准 KMP 的过程中很容易地处理出

那么要得到每一个 \(num[i]\) 我们只需要以 \(i\) 为起点,用 \(next()\) 不断迭代,直到 \(i'\le \frac i 2\),此时的 \(ans[i']\) 就是所求

每次都暴力迭代,复杂度显然无法接受,一种方案是对 \(next()\)\(2^j\) 次迭代结果倍增处理,较为麻烦

考虑在一个类似 KMP 的过程中动态维护 \(j\),但不同的是,每次处理完后,将 \(j\)\(next()\) 不断迭代,直到 \(j\le \frac i 2\),此时的 \(ans[j]\) 就是所求

#include <bits/stdc++.h>
using namespace std;

const int mod = 1e9+7;

char p[1000005];
int n,m,fail[1000005],ans[1000005],num[1000005];

void solve() {
    memset(p,0,sizeof p);
    memset(fail,0,sizeof fail);
    memset(ans,0,sizeof ans);
    memset(num,0,sizeof num);
	cin>>p+1;m=strlen(p+1);
	for(int i=2;i<=m;i++) {
		int j=fail[i-1];
		while(p[j+1]-p[i] && j) j=fail[j];
		if(p[j+1]==p[i]) ++j;
		fail[i]=j;
		if(j>0) ans[i]=ans[j]+1;
	}
	for(int i=1;i<=m;i++) ans[i]++;
	int j=0;
    for(int i=2;i<=m;i++) {
        while(p[j+1]-p[i] && j) j=fail[j];
        if(p[j+1]==p[i]) ++j;
        while(j*2>i && j) j=fail[j];
        num[i]=ans[j];
    }
    long long res=1;
    for(int i=2;i<=m;i++) res*=num[i]+1, res%=mod;
    cout<<res<<endl;
}

signed main() {
    ios::sync_with_stdio(false);
    int n;
    cin>>n;
    while(n--) solve();
}

相关文章: