题目链接:
题目.
简单一点来说,这个题就是求一个字符串的 num n u m 数组的和,其中有 num[i] n u m [ i ] 表示1~i中有多少个不交叉的相等的前缀和后缀 的数目,要求一个 O(n) O ( n ) 的做法
QwQ
感觉一看到这个题,其实没什么思路呀
从 KMP K M P 的角度出发,对于一个 i i 来说,显然都是他的后缀,所以,我们貌似可以维护一个 num1 n u m 1 表示,可以交叉的 相等的 前缀和后缀的数目
比较容易推出 num1[i]=num1[pre[i]]+1 n u m 1 [ i ] = n u m 1 [ p r e [ i ] ] + 1
这里可以理解成 在原来最多的数目上,再加上当前位匹配的贡献
因为你对于当前的i的一些前后缀的比较和计算,已经在 pre[i] p r e [ i ] 的时候计算过了
那么我们怎么计算这个题目要求的
num
n
u
m
呢
QwQ实际上对于每个
i
i
要找到一个小于的最小递归层数的
pre
p
r
e
(递归的意思是
pre[pre[pre[i]]]
p
r
e
[
p
r
e
[
p
r
e
[
i
]
]
]
)
如果我们对于每一个 i i 都暴力去做的话,时间复杂度肯定是不允许的,这时候我们就需要考虑一个性质:
我们在循环到时当前的
j
j
是不是可以重前一次的最长的不重叠的得到呢?答案是肯定的,要么小于等于上一次的
j
j
,要么等于上一次的
具体证明可以通过反证法来证明
QwQ然后就直接像 KMP K M P 那种跳的方式,跳 j j <script type="math/tex" id="MathJax-Element-1792">j</script>就可以的
感觉这个题很有意思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;
}