POI2012 PRE-Prefixuffix
P3546 [POI2012] PRE-Prefixuffix
题目大意
对于两个字符串 S 1 , S 2 S_1,S_2 S1?,S2?,如果将 S 1 S_1 S1?的一个后缀移动到开头后这个字符串变成了 S 2 S_2 S2?,则称 S 1 , S 2 S_1,S_2 S1?,S2?循环同构。
给定一个长度为 n n n的字符串 S S S,求满足下面条件的最大的 L L L:
- L L L不超过 n 2 \dfrac n2 2n?
- S S S长度为 L L L的前缀和 S S S长度为 L L L的后缀循环等价
1 ≤ n ≤ 1 0 6 1\leq n\leq 10^6 1≤n≤106
题解
题目要求的就是形如下面这样的前后缀,其中红色部分相同,绿色部分相同。
判断两个字符串相同可以用哈希。对于每个位置
i
i
i,我们用
f
i
f_i
fi?表示满足以下条件的的最大的
k
k
k:
- [ i , i + k ? 1 ] = [ n ? i ? k + 2 , n ? i + 1 ] [i,i+k-1]=[n-i-k+2,n-i+1] [i,i+k?1]=[n?i?k+2,n?i+1]
- i + k ? 1 ≤ n 2 i+k-1\leq \dfrac n2 i+k?1≤2n?
如果通过枚举 k k k来求每个 f i f_i fi?的话,时间复杂度是 O ( n 2 ) O(n^2) O(n2)的,我们考虑优化。
我们发现, f i ? 1 ≤ f i + 2 f_{i-1}\leq f_i+2 fi?1?≤fi?+2,证明如下:
假设 f i ? 1 > f i + 2 f_{i-1}>f_i+2 fi?1?>fi?+2,因为 f i ? 1 f_{i-1} fi?1?比 f i f_i fi?多了 i ? 1 i-1 i?1这个位置,如果将 f i ? 1 f_{i-1} fi?1?对应的绿色部分的第一个字符和最后一个字符去掉,那么一定符合 f i f_i fi?的条件,也就是说 f i f_i fi?至少为 f i ? 1 ? 2 f_{i-1}-2 fi?1??2,推得 f i ? 1 ≤ f i + 2 f_{i-1}\leq f_i+2 fi?1?≤fi?+2,与 f i ? 1 > f i + 2 f_{i-1}>f_i+2 fi?1?>fi?+2矛盾,得证 f i ? 1 ≤ f i + 2 f_{i-1}\leq f_i+2 fi?1?≤fi?+2。
根据这个条件,我们可以 O ( n ) O(n) O(n)求出所有 f i f_i fi?。
然后,对于每个 i i i,判断 [ 1 , i ] [1,i] [1,i]和 [ n ? i + 1 , n ] [n-i+1,n] [n?i+1,n]是否相同,如果相同就用 i + f i ? 1 i+f_i-1 i+fi??1更新答案。
时间复杂度为 O ( n ) O(n) O(n)。
code
#include<bits/stdc++.h>
using namespace std;
const int base=233;
const long long mod=1e9+7;
const int N=1000000;
int n,ans=0,f[N+5];
long long pw[N+5],g[N+5];
char s[N+5];
long long gt(int l,int r){
return (g[r]-g[l-1]*pw[r-l+1]%mod+mod)%mod;
}
int main()
{
scanf("%d",&n);
scanf("%s",s+1);
pw[0]=1;
for(int i=1;i<=n;i++){
g[i]=(g[i-1]*base+s[i]-'a'+1)%mod;
pw[i]=pw[i-1]*base%mod;
}
for(int i=n/2;i>=1;i--){
f[i]=min(f[i+1]+2,n/2-i+1);
while(f[i]>0&>(i,i+f[i]-1)!=gt(n-i-f[i]+2,n-i+1)) --f[i];
}
for(int i=1;i<=n/2;i++){
if(gt(1,i-1)==gt(n-i+2,n)) ans=max(ans,i+f[i]-1);
}
printf("%d",ans);
return 0;
}
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!