Manacher(马拉车算法)
Manacher算法主要用于求解回文串问题,能够统计出以每一个位置为中心的回文串的个数,效率极高。
模板
题目描述
Manacher算法的实现过程:
1.在字符串每两个字符之间插入一个分隔符。
2.iii从111到nnn求解P[i]P[i]P[i]表示以iii为中心的最长回文子串长度,同时记录一个当前延伸最远的回文子串,记它的右端点为rightrightright,中心点为midmidmid,则求P[i]P[i]P[i]时,若right>iright>iright>i,那么P[i]的长度至少为min(right−i,P[mid−(i−mid)])P[i]的长度至少为min(right-i,P[mid-(i-mid)])P[i]的长度至少为min(right−i,P[mid−(i−mid)]),然后再暴力地向两边拓展即可。
时间复杂度为均摊O(n)O(n)O(n)。
// luogu-judger-enable-o2
#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <string>
#include <cstring>
#include <ctime>
#include <cassert>
#include <string.h>
//#include <unordered_set>
//#include <unordered_map>
//#include <bits/stdc++.h>#define MP(A,B) make_pair(A,B)
#define PB(A) push_back(A)
#define SIZE(A) ((int)A.size())
#define LEN(A) ((int)A.length())
#define FOR(i,a,b) for(int i=(a);i<(b);++i)
#define fi first
#define se secondusing namespace std;template<typename T>inline bool upmin(T &x,T y) { return y<x?x=y,1:0; }
template<typename T>inline bool upmax(T &x,T y) { return x<y?x=y,1:0; }typedef long long ll;
typedef unsigned long long ull;
typedef long double lod;
typedef pair<int,int> PR;
typedef vector<int> VI;const lod eps=1e-11;
const lod pi=acos(-1);
const int oo=1<<30;
const ll loo=1ll<<62;
const int mods=1e9+7;
const int MAXN=20000005;
const int INF=0x3f3f3f3f;//1061109567
/*--------------------------------------------------------------------*/
inline int read()
{int f=1,x=0; char c=getchar();while (c<'0'||c>'9') { if (c=='-') f=-1; c=getchar(); }while (c>='0'&&c<='9') { x=(x<<3)+(x<<1)+(c^48); c=getchar(); }return x*f;
}
char st[MAXN],s[MAXN];
int P[MAXN];
int main()
{scanf("%s",st);int lenst=strlen(st);s[0]='%',s[1]='#';for (int i=0;i<lenst;i++) s[i*2+2]=st[i],s[i*2+3]='#';int lens=lenst*2+1;int mid=0,right=0;int smax=0;for (int i=1;i<=lens;i++){P[i]=(right>i?min(P[mid*2-i],right-i):1);while (s[i-P[i]]==s[i+P[i]]) P[i]++;if (right<i+P[i]) right=i+P[i],mid=i;smax=max(smax,P[i]);}printf("%d\n",smax-1);return 0;
}
一道例题
CF30E. Tricky and Clever Password
Solution
其实这并不是一道Manacher题,只是需要一步Manachcer预处理。
我们发现最后一个串suffixsuffixsuffix一定是原串的一个后缀,因此我们考虑枚举suffixsuffixsuffix的长度,找到最前面的一个prefixprefixprefix,使得prefixprefixprefix是suffixsuffixsuffix的反串,这一部分可以用KMP实现。
在找到prefix,suffixprefix,suffixprefix,suffix以后,我们还需要找一个最优的middlemiddlemiddle,它是一个长度为奇数的回文串,并包含在[preffix+1,suffix−1][preffix+1,suffix-1][preffix+1,suffix−1]之间,但我们直接求这里面的最长回文子串是不行的,因为其中的最长回文子串可能会与preffixpreffixpreffix或suffixsuffixsuffix重合,因此考虑二分答案,则问题变成了判断[preffix+1+mid,suffix−1−mid][preffix+1+mid,suffix-1-mid][preffix+1+mid,suffix−1−mid]之间是否有最长回文子串长度大于midmidmid。
这就变成了一个区间最长回文子串最大值,Manacher求出P[i]P[i]P[i]之后,STSTST表预处理就能够O(1)O(1)O(1)询问了。
总时间复杂度为O(nlgn)O(nlgn)O(nlgn)。
个人实现比较丑陋。。。
#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <string>
#include <cstring>
#include <ctime>
#include <cassert>
#include <string.h>
//#include <unordered_set>
//#include <unordered_map>
//#include <bits/stdc++.h>#define MP(A,B) make_pair(A,B)
#define PB(A) push_back(A)
#define SIZE(A) ((int)A.size())
#define LEN(A) ((int)A.length())
#define FOR(i,a,b) for(int i=(a);i<(b);++i)
#define fi first
#define se secondusing namespace std;template<typename T>inline bool upmin(T &x,T y) { return y<x?x=y,1:0; }
template<typename T>inline bool upmax(T &x,T y) { return x<y?x=y,1:0; }typedef long long ll;
typedef unsigned long long ull;
typedef long double lod;
typedef pair<int,int> PR;
typedef vector<int> VI;const lod eps=1e-11;
const lod pi=acos(-1);
const int oo=1<<30;
const ll loo=1ll<<62;
const int mods=998244353;
const int MAXN=600005;
const int INF=0x3f3f3f3f;//1061109567
/*--------------------------------------------------------------------*/
inline int read()
{int f=1,x=0; char c=getchar();while (c<'0'||c>'9') { if (c=='-') f=-1; c=getchar(); }while (c>='0'&&c<='9') { x=(x<<3)+(x<<1)+(c^48); c=getchar(); }return x*f;
}
struct Ansnode{ int l,mid,len,c; } Ans;
char s[MAXN],t[MAXN],St[MAXN];
PR mx[MAXN][20];
int Log[MAXN],nxt[MAXN],P[MAXN];
void InitKMP(char *st,int len)
{for (int i=2,j=0;i<=len;i++){while (j&&st[i]!=st[j+1]) j=nxt[j];if (st[i]==st[j+1]) j++;nxt[i]=j;
// cout<<"Nxt:"<<i<<" "<<nxt[i]<<endl;}
}
void Manacher(char *st,int len)
{St[0]='%',St[1]='#';for (int i=1;i<=len;i++) St[i<<1]=st[i],St[i<<1|1]='#';
// cout<<St<<endl;int Len=len<<1|1,mid=0,right=0;for (int i=1;i<=Len;i++){P[i]=(right>i?min(P[mid*2-i],right-i):1);while (St[i-P[i]]==St[i+P[i]]) P[i]++;
// cout<<i<<" "<<P[i]<<endl;if (right<i+P[i]) right=i+P[i],mid=i;}for (int i=1;i<=len;i++) {if (P[i*2]-1>=Ans.c) Ans=(Ansnode){0,i,0,P[i*2]-1};P[i]=P[i*2]>>1;
// cout<<P[i]<<endl;}
}
PR max(PR x,PR y){ return x.fi>=y.fi?x:y; }
void InitST(int n)
{Log[1]=0;for (int i=2;i<=n;i++) Log[i]=Log[i>>1]+1;for (int i=1;i<=n;i++) mx[i][0]=MP(P[i],i);for (int i=1;i<=Log[n];i++)for (int j=1;j<=n-(1<<i)+1;j++) mx[j][i]=max(mx[j][i-1],mx[j+(1<<(i-1))][i-1]);
}
PR querymx(int l,int r)
{if (l>r) return MP(-100000,0);int t=Log[r-l+1];return max(mx[l][t],mx[r-(1<<t)+1][t]);
}
PR getans(int L,int R)
{if (L>R) return MP(-100000,0);int l=1,r=(R-L)/2+1;while (l<r){int mid=(l+r+1)>>1;if (querymx(L+mid-1,R-mid+1).fi>=mid) l=mid;else r=mid-1;}return MP(l,querymx(L+l-1,R-l+1).se);
}
int main()
{scanf("%s",t+1);int len=strlen(t+1);for (int i=1;i<=len;i++) s[i]=t[len-i+1];InitKMP(s,len);Manacher(t,len);InitST(len);int ans=0;for (int l=1,i=1,j=0;l<=len;l++){
// cout<<l<<" "<<i<<" "<<j<<" "<<t[i+1]<<" "<<s[j+1]<<endl;for (;i<=len;i++){while (j&&t[i]!=s[j+1]) j=nxt[j];if (t[i]==s[j+1]) j++;if (j==l){PR q=getans(i+1,len-l);
// cout<<i+1<<" "<<len-l<<" "<<j<<":"<<q.fi<<" "<<q.se<<endl;if (q.fi+q.fi+l+l-1>Ans.c) Ans=(Ansnode){i-l+1,q.se,l,q.fi+q.fi+l+l-1};i++;break;}}}if (!Ans.len) { puts("1"); printf("%d %d\n",Ans.mid-(Ans.c-1)/2,Ans.c); }else { puts("3"); int q=Ans.c-Ans.len*2;printf("%d %d\n",Ans.l,Ans.len);printf("%d %d\n",Ans.mid-(q-1)/2,q);printf("%d %d\n",len-Ans.len+1,Ans.len);}return 0;
}
/*
xjowabclwjegohghlewcba
*/