题目链接:[http://uoj.ac/problem/5]
题解:直接求出next数组,发现对一段结尾的子串有+1贡献。前缀和差分统计即可。
exkmp代码
学习的这位大佬
//uoj 5 noi 2014
//exkmp 用处。求一个串的所有后缀与另一个串的最长公共前缀。
//本题只用了next数组的性质
#pragma GCC optimize(3)
#include<bits/stdc++.h>
using namespace std;
#define maxn 1000020
#define rep(i,l,r) for(register int i = l ; i <= r ; i++)
#define repd(i,r,l) for(register int i = r ; i >= l ; i--)
#define inf 1e8
typedef long long ll;
const ll mod = 1e9 + 7;
ll ans;
int n,T;
char s[maxn],t[maxn];
int next_[maxn]; //表示一个串与每个后缀的最长公共前缀
int ex[maxn]; //表示一个串与模板串所有后缀的最长公共前缀的长度
int sum[maxn];
void clear(){
rep(i,0,n) sum[i] = next_[i] = 0;
}
void getnext_(char *s){
int len = strlen(s),po = 1,i = 0,j; //po表示当前匹配前缀最长的后缀的起点
next_[0] = len;
//计算next_[1]
while ( s[i] == s[i + 1] && i + 1 < len ) i++;
next_[1] = i;
for (i = 2;i < len ; i++){
if ( next_[i - po] + i < next_[po] + po ) next_[i] = next_[i - po];//长度未超出匹配,可以直接得到next_[i]的值
else{ //next_的长度超出当前匹配,要继续匹配才能得到next_[i]的值
j = max(0,next_[po] + po - i); //从当前最远位置开始,如果i超出当前最远则从0开始
while ( i + j < len && s[j] == s[j + i] ) j++; //往后继续匹配
next_[i] = j;
po = i; //更新最远位置
}
}
// rep(i,0,len - 1) cout<<next_[i]<<" ";
// cout<<endl;
}
void exkmp(char *s,char *t){
int i = 0,j,po = 0,l1 = strlen(s),l2 = strlen(t);
getnext_(t);//计算子串的next_数组
while ( s[i] == t[i] && i < min(l1,l2) ) i++;
ex[0] = i; //计算ex[0]
for(i = 1 ; i < l1 ; i++){
if ( next_[i - po] + i < ex[po] + po ) ex[i] = next_[i - po];//第一种情况,直接可以得到ex[i]的值
else{//第二种情况,要继续匹配才能得到ex[i]的值
j = max(0,ex[po] + po - i);//如果i>ex[po]+po则要从头开始匹配从当前最远位置开始
while ( i + j < l1 && j < l2 && s[i + j] == t[j] ) j++;
ex[i] = j;
po = i;//更新po的位置
}
}
}
int main(){
freopen("input.txt","r",stdin);
scanf("%d",&T);
while ( T-- ){
clear();
scanf("%s",s);
n = strlen(s);
getnext_(s);
rep(i,0,n - 1){
int len = min(next_[i],(int)i);
sum[i]++ , sum[i + len]--;
}
int cur = 0; ans = 1;
rep(i,0,n - 1){
cur += sum[i];
ans = ans * (cur + 1) % mod;
}
printf("%lld\n",ans);
}
return 0;
}