bzoj 2803 [Poi2012]Prefixuffix 兼字符串hash入门
打cf的时候遇到的问题,clairs告诉我这是POI2012 的原题。。原谅我菜没写过。。于是拐过来写这道题并且学了下string hash。
字符串hash基于Rabin-Karp算法,并且对于各种长度子串的匹配是常数的。具体做法看代码就明白了。然后如果单hash没发过。那就试试双hash把。如果还不行那就三hash以此类推。。一般到双hash就基本不会出错。你的hash的mod值要尽量分开和是素数就对了。
这种方法实在是神,当数据过大时才会可能出错2333很适合竞赛。
附加一个类似双hash的做法,zy大神的做法。就是一个hash还是正常的hash,另一个hash则被替代成了这两段子串的ascii总和的比较。这个做法也是很不错的。
推荐07年杨弋的论文《Hash在信息学竞赛中的一类应用》,对hash应用讲解很不错。
2803: [Poi2012]Prefixuffix
Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 434 Solved: 175
[Submit][Status][Discuss]
Description
对于两个串S1、S2,如果能够将S1的一个后缀移动到开头后变成S2,就称S1和S2循环相同。例如串ababba和串abbaab是循环相同的。
给出一个长度为n的串S,求满足下面条件的最大的L:
1. L<=n/2
2. S的L前缀和S的L后缀是循环相同的。
Input
第一行一个正整数n (n<=1,000,000)。第二行n个小写英文字母,表示串S。
Output
一个整数,表示最大的L。
Sample Input
15
ababbabababbaab
ababbabababbaab
Sample Output
6
HINT
Source
代码其实大同小异。。hash代码长得都差不多,我双hash过得。
首先如果是循环同构,匹配的两串中一串相当于另一个串的左端部分的串被取出放到了右端。例如abcdefg和cdefgab。那么就题目给出的字符串,我们查找同构的前缀和后缀,同构的字符串就可以分为两部分,一部分就是前面说的左端换到右端的串,这两串是相同的,另一部分就是不动的串。因此我们枚举所有可能被换到右端的长度i,先检查转换串[1,i]和[i,n-i+1]是否匹配,然后计算[i+1,n-i]前后缀最长匹配长度f[i+1]。这样算出来的f[i+1]+i最大的即为答案。
而我们可以发现f[i]≤f[i+1]+2,因为[i,n-i+1]比[i+1,n-i]首尾各多了两个字符,对应最长匹配的串首和尾。自己纸上写写就知道了。
因此我们i从n/2枚举到1,得出最长的那个即为答案。
注意L≤n/2,所以你这个同构串是不能重叠的。
1 #include<bits/stdc++.h> 2 #define clr(x) memset(x,0,sizeof(x)) 3 #define clr_1(x) memset(x,-1,sizeof(x)) 4 #define LL long long 5 //usually mod num is 1004535809 99824435 1000000007 6 #define pnum 233 7 #define INF 0x3f3f3f3f 8 using namespace std; 9 const int N=1e6+10; 10 int f[N],power[N][2],hash[N][2],mod[2]={1004535809,99824435}; 11 inline int gethash(int l,int r,int x) 12 { 13 return (hash[r][x]-(LL)hash[l-1][x]*power[r-l+1][x]%mod[x]+mod[x])%mod[x]; 14 } 15 inline bool check(int x,int y,int len) 16 { 17 return gethash(x,x+len-1,0)==gethash(y,y+len-1,0) && gethash(x,x+len-1,1)==gethash(y,y+len-1,1); 18 } 19 int n,ans; 20 char s[N]; 21 int main() 22 { 23 scanf("%d",&n); 24 scanf("%s",s); 25 power[0][0]=power[0][1]=1; 26 hash[0][0]=hash[0][1]=0; 27 for(int i=1;i<=n;i++) 28 for(int j=0;j<2;j++) 29 { 30 power[i][j]=(LL)power[i-1][j]*pnum%mod[j]; 31 hash[i][j]=((LL)hash[i-1][j]*pnum+(s[i-1]-'a'))%mod[j]; 32 } 33 f[n/2+1]=0; 34 ans=0; 35 for(int i=n/2;i>=1;i--) 36 { 37 f[i]=min(f[i+1]+2,n/2-i+1); 38 while(f[i] && !check(i,n-i-f[i]+2,f[i])) f[i]--; 39 if(check(1,n-i+2,i-1)) 40 ans=max(f[i]+i-1,ans); 41 } 42 printf("%d\n",ans); 43 return 0; 44 }