CF 7D 字符串哈希 回文
转自
http://roba.rushcj.com/?p=439
定义k-回文串如下:(1)任何串(包括空串)都是0-回文;
(2)一个长度为n的回文串,若它的前n/2个字符和后n/2个字符都是(k-1)-回文,
则它是k-回文。现给定一个串(长度不超过5,000,000),
设它的每个前缀分别是x-回文,
求所有这些x值的和。
比如abacaba,
”a”是1-回文,
”aba”是2-回文,
”abacaba”是3-回文,故输出6。
将字符串 反转接在后面,然后KMP求解即可
把原串翻转后拼接在原来的串后面,用一个不在字母表中的分隔符隔开的。
然后用KMP里对模式串的预处理那个过程,求出失配以后往前跳的那个指针。
我们从新串最后一个位置开始,每向前跳一次都得到一个回文前缀。
比如有这样一个串 abaaba…..(后面部分省略),拼接后得到
abaaba......#......abaaba
012345......#......6789AB
则最后位置的失配指针为5,由KMP的意义,也就是说(012345) == (6789AB),而同时由我们的翻转操作知(012345)==(BA9876),
故(012345)为一个回文串。从5继续向前跳到2,同理(012)也是回文串。
由上过程可以把全部回文前缀作好标记。然后再从左到右简单地扫描一遍就可以把每个k值计算出来了。
Ps:幸亏CF内存不怎么限制,开了三个1KW的数组啊。。
View Code
#include<cstdio> #include<cstring> int n,m; char b[10000010]; int p[10000010]; int d[10000010]; void getp(){ p[1]=0; int i,j=0; for(i=2;i<=m;i++){ while(j>0&&b[j+1]!=b[i]) j=p[j]; if(b[j+1]==b[i]) j+=1; p[i]=j; } } int main() { scanf("%s",b+1); m=strlen(b+1); int x=m; b[m+1]='#'; for(int i=1;i<=m;i++) { b[m+1+i]=b[m+1-i]; } b[2*m+2]='\0'; m=2*m+1; getp(); int tmp=p[m]; while(tmp) { d[tmp]=1; tmp=p[tmp]; } __int64 ans=0; for(int i=1;i<=m;i++) { if(d[i]) d[i]=d[i/2]+1; ans+=d[i]; } if(ans==0) printf("1\n"); else printf("%I64d\n",ans); }
View Code
一种更简单的方法:哈希 /* l: s[0]*107^2+s[1]*107+s[2];//从前往后哈希 r: s[0]+s[1]*107+s[2]*107^2;//从后往前哈希 */ #include <iostream> #include <cstdio> using namespace std; char s[5000010]; int f[5000010]; int main(){ scanf("%s", s); int l=0, r=0, e=1, ans=0; for (int i=0; s[i]; i++){//l,r:两个哈希值,一个从左到右,一个从右到左 l=l*103+s[i]; r=r+s[i]*e; e*=103; // printf("%d %d\n",l,r) ; if (l==r) f[i+1]=f[(i+1)/2]+1; ans+=f[i+1]; } cout<<ans<<endl; }