BZOJ 2565 最长双回文串 哈希+二分+线扫+树状数组
2017-10-08 07:06
453 查看
Description
[align=left]顺序和逆序读起来完全一样的串叫做回文串。比如acbca是回文串,而abc不是(abc的顺序为“abc”,逆序为“cba”,不相同)。输入长度为n的串S,求S的最长双回文子串T,即可将T分为两部分X,Y,(|X|,|Y|≥1)且X和Y都是回文串。[/align]
Input
一行由小写英文字母组成的字符串S。Output
[align=left]一行一个整数,表示最长双回文子串的长度。[/align]Sample Input
baacaabbacabbSample Output
12HINT
样例说明从第二个字符开始的字符串aacaabbacabb可分为aacaa与bbacabb两部分,且两者都是回文串。
对于100%的数据,2≤|S|≤10^5
2015.4.25新加数据一组
网上都是用manacher搞回文串的……可是我不会啊= =
求回文串可以把串反过来然后哈希,
接着二分一个长度,判断两段是否相等即可判断回文。
处理奇偶性的问题,可以间隔加入'{'
比如aaaa,就变成{a{a{a{a{
那么都是奇数了。
预处理出所有的最长的回文串,然后枚举断点。
假如一个断点pos,(pos-1)及左边是左边的回文串,pos及右边是右边的回文串,
加入断点pos的左边有某个回文串的mid,
根据回文串的特性,比如aaaaa,1~5,2~4,3~3都是回文串
所以事实上只需要保存以一个点为中点的最长回文串就够了,
然后如果有下面的关系(某一个回文串左端点是l,右是r,中点是mid):
那么(mid-(pos-mid))~pos也是一个回文串,
根据这个性质,也就是说我们需要维护两个:
1.所有r>=pos的mid在pos左边的最小mid;
2.所有l<=pos的mid在pos右边的最大mid。
对于1,只要把所有得出的回文串按照r从大到小排序,
然后从后往前枚举pos,并且用一个单调的指针维护1中的最小mid;
(单调指针,就是因为r有序,所以每次当指针point,r[point]>=pos,就point++,
因为都是从大到小的,所以point只会单调……)
对于2似乎没什么好的办法,但是看到是l<pos的最大mid,
就可以考虑到树状数组了。
在指针point移到某一个回文串,就在树状数组里更新维护2的最大mid。
同时在每个pos统计答案即可。
不是很复杂的想法但是还是有一点细节。
主要是奇偶性,以及转为奇数串、知道最小最大mid后的答案计算方法。
具体可以看代码……
方法比较麻烦,但是时间还是主要在hash上,,
manacher比较优越。
#include<bits/stdc++.h> #define ll long long using namespace std; const int seed=1311,Mod=234065077, N=200005; int n,len,tr ; int fac ,hash[2] ; char s ; struct HuiWen{int l,mid,r;}hw ; bool cmp(HuiWen x,HuiWen y){return x.r>y.r;} void update(int x,int y){ for (;x<=len;x+=x&-x) if (hw[tr[x]].mid<hw[y].mid) tr[x]=y; } int get(int x){ int y=0; for (;x;x-=x&-x) if (hw[y].mid<hw[tr[x]].mid) y=tr[x]; return y; } void HASH(char s[],int opt){ hash[opt][0]=0,fac[0]=1; for (int i=1;i<=len;i++) fac[i]=(ll)fac[i-1]*seed%Mod; for (int i=1;i<=len;i++) hash[opt][i]=((ll)hash[opt][i-1]*seed%Mod+s[i]-'a')%Mod; } int HASHNUM(int opt,int x,int y){ int t1=(ll)hash[opt][x-1]*fac[y-x+1]%Mod, t2=hash[opt][y]; return (t2-t1+Mod)%Mod; } int BS(int m){ int l=0,r=min(len-m,m-1),mid; while (l<r){ mid=(l+r+1)>>1; if (HASHNUM(0,m-mid,m-1)==HASHNUM(1,len-(m+mid)+1,len-m)) l=mid; else r=mid-1; } return l; } void Pre(){ char s1[N<<1]; for (int i=1;i<=len;i++) s1[i]=s[i]; for (int i=1;i<=len;i++) s[(i<<1)-1]='{',s[i<<1]=s1[i]; len=len<<1|1,s[len]='{'; for (int i=1;i<=len;i++) s1[i]=s[len-i+1]; HASH(s,0),HASH(s1,1); n=0; for (int i=1;i<=len;i++){ int t=BS(i); hw[++n].l=i-t,hw .r=i+t,hw .mid=i; } } void solve(){ sort(hw+1,hw+1+n,cmp); int MINmid=N+1,MINI=0,pj=1,ans=0; for (int i=len;i;i--){ if (s[i]!='{') continue; while (pj<=n && hw[pj].r>=i-1){ update(hw[pj].l,pj); if (MINmid>hw[pj].mid) MINmid=min(MINmid,hw[pj].mid),MINI=pj; pj++; } int t=get(i),t1=0,t2=0; if (hw[t].mid>=i && MINmid<=i-1){ t1=(hw[t].mid-i)>>1; if (!(hw[t].mid&1)) t1++,t1=(t1<<1)-1; else t1<<=1; t2=(i-MINmid)>>1; if (!(MINmid&1)) t2++,t2=(t2<<1)-1; else t2<<=1; } ans=max(ans,t1+t2); } printf("%d\n",ans); } int main(){ scanf("%s",s+1); len=strlen(s+1); Pre(); solve(); return 0; }
相关文章推荐
- BZOJ 2565 最长双回文串
- bzoj2565 最长双回文串
- [bzoj2565]最长双回文串 manacher
- BZOJ 2565 最长双回文串 (Manacher)
- 【BZOJ2565】最长双回文串 Manacher
- BZOJ 2565 最长双回文串
- [bzoj 2565] 最长双回文串
- BZOJ 2565 最长双回文串
- 最长双回文串 bzoj 2565 回文自动机
- BZOJ 2565 最长双回文串(哈希)
- bzoj 2565: 最长双回文串 manachar
- bzoj 2565: 最长双回文串
- bzoj2565 最长双回文串 manacher
- 【BZOJ】【2565】最长双回文串
- bzoj 2565 最长双回文串 manacher
- BZOJ2565 最长双回文串
- BZOJ[2565]最长双回文串 Manacher
- BZOJ2565:最长双回文串(回文自动机)
- bzoj2565 最长双回文串
- BZOJ 2565 最长双回文串(manacher)