您的位置:首页 > 其它

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

baacaabbacabb

Sample Output

12

HINT

样例说明

从第二个字符开始的字符串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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: