bzoj2565: 最长双回文串
题意
顺序和逆序读起来完全一样的串叫做回文串。比如acbca是回文串,而abc不是(abc的顺序为“abc”,逆序为“cba”,不相同)。
输入长度为n的串S,求S的最长双回文子串T,即可将T分为两部分X,Y,(|X|,|Y|≥1)且X和Y都是回文串。 N<=100000。
这道题可以用manacher算法解决,但是用manacher解决问题的同时,有可能需要计算长度的关系,可能会出现混乱。在这里采用更清楚的回文自动机;
在做这道题之前需要掌握的知识是回文自动机的实现和作用,如果有不了解算法的同志,请自行翻大神博客进行学习:
https://www.cnblogs.com/crazyacking/p/5234823.html
在这里,由于是要找两个回文串,还必须是相邻的,所以我们可以通过把整串翻转的方法实现求相邻回文串,根据回文树的作用,可以求出以下标i结尾的最长回文字符串,注意是以下标i结尾的最长串,而不是开始,不理解的话可以自己模拟一下字符加进回文自动机形成回文自动机的那些步骤,求出来之后可以枚举相邻的那个点来进行转移,求出最长的双回文串,这道题就结束了,现在上代码:
1 #include<iostream> 2 #include<cmath> 3 #include<cstdio> 4 #include<cstdlib> 5 #include<algorithm> 6 #include<cstring> 7 #include<string> 8 #include<queue> 9 using namespace std; 10 const int MAXN=100005; 11 int nxt[MAXN][26],fail[MAXN],len[MAXN],num[MAXN],cnt[MAXN],mx1[MAXN],mx2[MAXN]; 12 int n,m,k,l,r,last,p=0; 13 char s[MAXN],ops[MAXN]; 14 inline int getfail(int x) 15 { 16 while(s[n-len[x]-1]!=s[n]) x=fail[x]; 17 return x; 18 } 19 inline void add(int x,int i,int *mx) 20 { 21 last=getfail(last); 22 if(nxt[last][x]==0){ 23 len[++p]=len[last]+2; 24 fail[p]=nxt[getfail(fail[last])][x]; 25 nxt[last][x]=p; 26 num[p]=num[fail[p]]+1; 27 } 28 last=nxt[last][x]; 29 mx[i]=len[last]; 30 cnt[last]++; 31 return ; 32 } 33 int main() 34 { 35 scanf("%s",s+1); 36 m=strlen(s+1); 37 p=last=n=0; 38 len[++p]=-1; 39 fail[0]=p; 40 for(int i=1;i<=m;i++){ 41 n++; 42 add(s[i]-'a',i,mx1); 43 } 44 memset(fail,0,sizeof(fail)); 45 memset(cnt,0,sizeof(cnt)); 46 memset(num,0,sizeof(num)); 47 memset(len,0,sizeof(len)); 48 memset(nxt,0,sizeof(nxt)); 49 p=last=n=0;len[++p]=-1;fail[0]=p; 50 for(int i=1;i<=m;i++){ 51 ops[i]=s[m-i+1]; 52 } 53 for(int i=1;i<=m;i++){ 54 s[i]=ops[i]; 55 } 56 for(int i=1;i<=m;i++){ 57 n++; 58 add(s[i]-'a',i,mx2); 59 } 60 int ans=-1; 61 for(int i=1;i<=m;i++){ 62 ans=max(ans,mx1[i]+mx2[m-i+0]); 63 } 64 cout<<ans<<endl; 65 return 0; 66 }