$luogu2375[NOI2014]$

(problem)

其中,(next[i],next[next[i]],next[next[next[i]]]......)都是这个前缀串i的公共前后缀,而且只有它们是公共前后缀

那么,我们其实只要在求(next)的过程中,顺便把这个公共前后缀的数量递推一下,就得到了一个弱化版的(num数组):可以重叠的公共前后缀数量,我们称之为(ans) 如何去除有重叠的?
还是看上面那张图

首先next数组有一个性质:(next[i] < inext[i]<i)
也就是说,一旦有一个递归了(n)层的(next),比原前缀i的长度的一半要小,那么这个(next)的递推出的答案(ans)就是(i)(num)

一个问题
假如我们拿到的串是(1e6个'a'),那么上面那个算法就会被卡成O((n^2))道理的话大家可以想一想(每一次递归都只会把(next[i])变小1)

那么我们需要做一个优化,来解决这个问题,而解决问题的核心就是:(减少重复递归)

减少重复递归......(有没有想到什么?)

没错,就是如同求(next)时一样的方法!

我们将递归用的变量(j)的值不更新,这样,求完了i的答案以后,(j)的位置一定在(i/2)的左边,也就是它已经满足要求了

这时再递归求解,总时间效率是(O(n))

#include <bits/stdc++.h>
using namespace std;
const ll MOD=1e9+7;
int n,fail[1000010],ans[1000010];
ll cnt;
char a[1000010];
int main() {
	int T,i,j;
	scanf("%d",&T);
	while(T--) {
		scanf("%s",a);
		n=strlen(a);
		memset(fail,0,sizeof(fail));
		j=0;
		ans[0]=0;
		ans[1]=1;
		for(i=1; i<n; i++) {
			while(j&&(a[i]!=a[j])) j=fail[j];
			j+=(a[i]==a[j]);
			fail[i+1]=j;
			ans[i+1]=ans[j]+1;
		}
		j=0;
		cnt=1;
		for(i=1; i<n; i++) {
			while(j&&(a[i]!=a[j])) j=fail[j];
			j+=(a[i]==a[j]);
			while((j<<1)>(i+1)) j=fail[j];
			cnt=(cnt*(ll)(ans[j]+1))%MOD;
		}
		printf("%lld
",cnt);
	}
	return 0 ;
}
不存在十全十美的文章 如同不存在彻头彻尾的绝望
原文地址:https://www.cnblogs.com/qf-breeze/p/10551471.html