最长双回文串
看过题之后,感觉似乎最长双回文串是可以由两个回文串拼接出来的?
于是乎我就有了一种算法!每次用manacher求出当前最大回文半径的同时,处理出每个一个点向右能拓展的最远的回文串长度和向左能拓展的回文串长度,就有如下递推:
mr[i-p[i]+1] = max(mr[i-p[i]+1],p[i]-1);
ml[i+p[i]-1] = max(ml[i+p[i]-1],p[i]-1);
之后我兴致冲冲的用p[i] + max(mr[i+p[i]],ml[i-p[i]])去更新答案,然后WA了。
因为答案有可能不是会选取每个最长的回文串,你这样默认选取了至少一个当前最长的回文串。
难道我们的拼接大业就完不成了吗?不,不会的!
我们发现通过ml,mr的性质可以递推出,一个点能向左/右拓展的最远的回文串,等于其后/前一个点能拓展出的最长长度-1.这样的话,我们就能通过递推求出每一个点的情况,也就不会每次强制性取一个最大值了。
然后我们用每个点两者之和更新答案即可。
#include<iostream> #include<cstdio> #include<cmath> #include<algorithm> #include<queue> #include<cstring> #define rep(i,a,n) for(int i = a;i <= n;i++) #define per(i,n,a) for(int i = n;i >= a;i--) #define enter putchar('\n') #define pr pair<int,int> #define mp make_pair #define fi first #define sc second using namespace std; typedef long long ll; const int M = 100005; const int N = 10000005; int read() { int ans = 0,op = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') op = -1; ch = getchar(); } while(ch >='0' && ch <= '9') { ans *= 10; ans += ch - '0'; ch = getchar(); } return ans * op; } char s[M<<1],c[M]; int p[M<<1],ml[M<<1],mr[M<<1],mx,mid,len,ans; int change() { int l = strlen(c),j = 2; s[0] = '!',s[1] = '#'; rep(i,0,l-1) s[j++] = c[i],s[j++] = '#'; s[j] = '&'; return j; } void manacher() { len = change(),mx = mid = 1; rep(i,1,len-1) { if(i < mx) p[i] = min(mx-i,p[(mid<<1)-i]); else p[i] = 1; while(s[i-p[i]] == s[i+p[i]]) p[i]++; if(mx < i + p[i]) mid = i,mx = i + p[i]; mr[i-p[i]+1] = max(mr[i-p[i]+1],p[i]-1); ml[i+p[i]-1] = max(ml[i+p[i]-1],p[i]-1); } } int main() { scanf("%s",c); manacher(); //rep(i,1,len-1) printf("%d ",p[i]-1);enter; //rep(i,1,len-1) printf("%d ",mr[i]);enter; //rep(i,1,len-1) printf("%d ",ml[i]);enter; rep(i,1,len) mr[i] = max(mr[i],mr[i-1]-1); per(i,len,1) ml[i] = max(ml[i],ml[i+1]-1); rep(i,1,len) ans = max(ans,ml[i] + mr[i]); printf("%d\n",ans); return 0; }
当你意识到,每个上一秒都成为永恒。