题目链接
http://www.lydsy.com/JudgeOnline/problem.php?id=3670
思路
为了方便叙述,下面所说的前缀 i 就是字符串中的区间 [1,i] 对应的子串
根据KMP定义, next[i]=j 表示 前缀j 满足既是前缀 i 的前缀,又是前缀 i 的后缀,且 j 是最大的。(其实题目里对此也有说明)
然后我们可以根据 next[] 数组构造一个 cnt[] 数组, cnt[i]= 满足既是前缀 i 的前缀,又是前缀 i 的后缀的子串个数,这个数组可以通过递推在 O(n) 时间内得出:
cnt[1]=1
cnt[i]=cnt[next[i]]+1
然后我们可以通过 cnt[]和next[] 数组得到 num[] 数组。具体做法是,对于每个 i>=2 ( i=1 时显然 num[i]=0 ),通过沿着 next[] 指针向前走找到最大的 j , j 满足前缀 j 既是前缀 i 的前缀,也是它的后缀,且 2j<=i (即题面中所限制的前缀和后缀不重叠),那么 num[i]=cnt[j] 。
代码
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#define MAXN 1000100
#define MOD 1000000007
using namespace std;
typedef long long int LL;
LL ans=0;
char s[MAXN];
int next[MAXN],cnt[MAXN]; //cnt[i]=既是1~i的后缀又是它的前缀的字符串个数
void getnext(char str[],int len)
{
int k=0;
next[1]=0;
cnt[1]=1; //!!!!!!
for(int i=2;i<=len;i++)
{
while(k>0&&str[k+1]!=str[i]) k=next[k];
if(str[k+1]==str[i]) k++;
next[i]=k;
cnt[i]=cnt[k]+1;
}
}
LL work(char str[],int len)
{
int k=0;
ans=1;
for(int i=2;i<=len;i++) //用next[]和cnt[]数组求出num[i]
{
while(k&&str[k+1]!=str[i]) k=next[k];
if(str[k+1]==str[i]) k++;
while((k*2)>i) k=next[k];
ans=(ans*(cnt[k]+1))%MOD;
}
return ans;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%s",s+1);
int len=strlen(s+1);
getnext(s,len);
printf("%lld\n",work(s,len));
}
return 0;
}