BZOJ2803[Poi2012]Prefixuffix——hash
题目描述
对于两个串S1、S2,如果能够将S1的一个后缀移动到开头后变成S2,就称S1和S2循环相同。例如串ababba和串abbaab是循环相同的。
给出一个长度为n的串S,求满足下面条件的最大的L:
1. L<=n/2
2. S的L前缀和S的L后缀是循环相同的。
输入
第一行一个正整数n (n<=1,000,000)。第二行n个小写英文字母,表示串S。
输出
一个整数,表示最大的L。
样例输入
15
ababbabababbaab
ababbabababbaab
样例输出
6
假设两个串是循环同构的,那么这两个串可以表示成x+y和y+x的形式(其中x,y为两个字符串)
设f[i]表示前后都去掉i个字符后能匹配的最长前后缀长度,即y的最长长度。
手画一下能够发现对于位于左一半的i和i+1,f[i]<=f[i+1]+2。如果大于了就说明f[i+1]还能更大。
那么就可以从中间向开头转移f[i],最后求出i+f[i]的最大值即可。这道题卡自然溢出。
#include<set> #include<map> #include<queue> #include<stack> #include<cmath> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define ll long long using namespace std; ll h[1000010]; ll g[1000010]; ll base1[1000010]; ll base2[1000010]; char ch[1000010]; int mod=1e9+7; int n; int f[1000010]; int ans=0; bool check(int x,int y,int l) { ll s1,s2,t1,t2; s1=((h[x+l-1]-h[x-1]*base1[l])%mod+mod)%mod; s2=((g[x+l-1]-g[x-1]*base2[l])%mod+mod)%mod; t1=((h[y+l-1]-h[y-1]*base1[l])%mod+mod)%mod; t2=((g[y+l-1]-g[y-1]*base2[l])%mod+mod)%mod; if(s1==t1&&s2==t2) { return true; } return false; } int main() { scanf("%d",&n); scanf("%s",ch+1); base1[0]=1; base2[0]=1; for(int i=1;i<=n;i++) { base1[i]=base1[i-1]*233%mod; base2[i]=base2[i-1]*2333%mod; h[i]=(h[i-1]*233+(ch[i]-96))%mod; g[i]=(g[i-1]*2333+(ch[i]-96))%mod; } for(int i=n/2;i>=1;i--) { f[i]=min(f[i+1]+2,(n-2*i)/2); while(f[i]&&(!check(i+1,n-f[i]+1-i,f[i]))) { f[i]--; } } for(int i=1;i<=n/2;i++) { if(!check(1,n-i+1,i)) { continue; } ans=max(ans,f[i]+i); } printf("%d",ans); }