题目:
题解:
首先他给了我们关于next数组的提示,这挺好的。
先看到 num 的定义:不互相重叠的公共前后缀个数
说明 num不同于 next 记录的是一个最大值,它记录的是一个和值
那么我们先用kmp数组用递推求出ans[i]表示前缀后缀可以重叠的最长公共部分长度,这个可以用j蹦完之后直接ans[i]+1,加的1就是可以有个新的整体咯。
怎么去除重叠的呢?一旦有一个递归了n层的next,比原前缀i的长度的一半要小,那么这个next的递推出的答案 ans 就是i的num了。这个可以感性理解
假如我们拿到的串是1e6个’a’,那么上面那个算法就会被卡成 O(n2) O ( n 2 ) ,道理的话大家可以想一想(每一次递归都只会把next[i]变小1)
那么我们需要做一个优化,来解决这个问题,而解决问题的核心就是:减少重复递归
就是如同求 next 时一样的方法,我们不每次更新递归用的变量 j ,这样,求完了 i 的答案以后, j 的位置一定在 i2 i 2 的左边,也就是它已经满足要求了。每次j最多往右移动一位,即j++,这样保证他一直在左边。
这时再递归求解,总时间效率是 O(n) O ( n ) 的
代码:
#include <cstdio>
#include <cstring>
#define LL long long
using namespace std;
const int mod=1e9+7;
const int N=1000005;
int l,t[N],ans[N];char st[N];
void build()
{
t[0]=-1;
for (int i=0;i<l;i++)
{
int j=t[i];
while (j!=-1 && st[j]!=st[i]) j=t[j];
t[i+1]=++j; ans[i+1]=ans[j]+1;
}
}
int main()
{
int T;scanf("%d",&T);
while (T--)
{
scanf("%s",st);
l=strlen(st);ans[1]=1;
build();
LL cnt=1;int j=0;
for (int i=1;i<l;i++)
{
while (j!=-1 && st[j]!=st[i]) j=t[j];
j++;
while (j*2>i+1) j=t[j];
cnt=cnt*(LL)(ans[j]+1)%mod;
}
printf("%lld\n",cnt);
}
}