题目链接:
题目.
简单一点来说,这个题就是求一个字符串的(num)数组的和,其中有(num[i])表示1~i中有多少个不交叉的相等的前缀和后缀 的数目,要求一个(O(n))的做法
QwQ
感觉一看到这个题,其实没什么思路呀
从(KMP)的角度出发,对于一个(i)来说,显然(pre[i],pre[pre[i]])都是他的后缀,所以,我们貌似可以维护一个(num1)表示,可以交叉的 相等的 前缀和后缀的数目
比较容易推出(num1[i]=num1[pre[i]]+1)
这里可以理解成 在原来最多的数目上,再加上当前位匹配的贡献
因为你对于当前的i的一些前后缀的比较和计算,已经在(pre[i])的时候计算过了
那么我们怎么计算这个题目要求的(num)呢
QwQ实际上对于每个(i)要找到一个小于(frac{i}{2})的最小递归层数的(pre) (递归的意思是(pre[pre[pre[i]]]))
如果我们对于每一个(i)都暴力去做的话,时间复杂度肯定是不允许的,这时候我们就需要考虑一个性质:
我们在循环到(i)时当前的(j)是不是可以重前一次的最长的不重叠的(j)得到呢?答案是肯定的,要么小于等于上一次的(j),要么等于上一次的(j+1)
具体证明可以通过反证法来证明
QwQ然后就直接像(KMP)那种跳的方式,跳(j)就可以的
感觉这个题很有意思QwQ而且我还不是很懂呀
留个坑吧
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
const long long mod = 1e9+7;
const int maxn = 1e6+1e2;
int pre[maxn],num[maxn];
char s[maxn];
char s1[maxn];
long long ans=0;
int n;
int t;
void init()
{
for (int i=1;i<=maxn-10;i++) s[i]=s1[i];
memset(pre,0,sizeof(pre));
ans=1;
memset(num,0,sizeof(num));
}
int main()
{
cin>>t;
while (t--)
{
init();
scanf("%s",s+1);
n=strlen(s+1);
pre[1]=0;
num[1]=1;
for (int i=2;i<=n;i++)
{
int j=pre[i-1];
while (j && s[j+1] !=s[i]) j=pre[j];
if (s[j+1]==s[i]) j++;
pre[i]=j;
num[i]=num[j]+1;
}
int j=0;
for (int i=1;i<=n;i++)
{
while (j && s[j+1] !=s[i]) j=pre[j];
if (s[j+1]==s[i]) j++;
while ((j << 1)>=i+1) j=pre[j];
ans=ans*(num[j]+1)%mod;
}
cout<<ans<<endl;
}
return 0;
}