题目大意:给定一个字符串,求一个最长的子串,该字串可以分解为两个回文子串
傻逼的我又忘了Manacher怎么写了= = 无奈Hash+二分吧
首先将字符串用分隔符倍增,然后求出以每个点为中心的最长回文半径
然后考虑两个回文串怎么合并成一个
我们发现图中以i为中心的回文串和以j为中心的回文串合并后长度恰好为(j-i)*2
能合并的前提是以两个点为中心的回文串有交点
那么对于每个j我们要求出有交点的最左侧的i
维护一个后缀min随便搞搞就可以了
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define M 200200 #define BASE 131 using namespace std; typedef unsigned long long ll; int n,ans; int f[M],min_pos[M]; char s[M]; ll hash1[M],hash2[M],power[M]; bool Judge(int mid,int len) { ll _hash1=hash1[mid+len-1]-hash1[mid-1]*power[len]; ll _hash2=hash2[mid-len+1]-hash2[mid+1]*power[len]; return _hash1==_hash2; } int Bisecition(int x) { int l=1,r=min(x,n-x+1); while(l+1<r) { int mid=l+r>>1; if( Judge(x,mid) ) l=mid; else r=mid; } return Judge(x,r)?r:l; } int main() { int i; static char str[M]; scanf("%s",str+1); for(s[1]='#',i=1;str[i];i++) s[i<<1]=str[i],s[i<<1|1]='#'; n=strlen(s+1); for(power[0]=1,i=1;i<=n;i++) power[i]=power[i-1]*BASE; for(i=1;i<=n;i++) hash1[i]=hash1[i-1]*BASE+s[i]; for(i=n;i;i--) hash2[i]=hash2[i+1]*BASE+s[i]; memset(min_pos,0x3f,sizeof min_pos); for(i=1;i<=n;i++) { f[i]=Bisecition(i); min_pos[i+f[i]-1]=min(min_pos[i+f[i]-1],i); } for(i=n-1;i;i--) min_pos[i]=min(min_pos[i],min_pos[i+1]); for(i=1;i<=n;i++) { int temp=min_pos[i-f[i]+1]; ans=max(ans,i-temp<<1); } cout<<(ans>>1)<<endl; return 0; }
原文地址:http://blog.csdn.net/popoqqq/article/details/43925833