Manacher(马拉车算法)
Manacher算法主要用于求解回文串问题,能够统计出以每一个位置为中心的回文串的个数,效率极高。
模板
Manacher算法的实现过程:
1.在字符串每两个字符之间插入一个分隔符。
2.
i
i
i从
1
1
1到
n
n
n求解
P
[
i
]
P[i]
P[i]表示以
i
i
i为中心的最长回文子串长度,同时记录一个当前延伸最远的回文子串,记它的右端点为
r
i
g
h
t
right
right,中心点为
m
i
d
mid
mid,则求
P
[
i
]
P[i]
P[i]时,若
r
i
g
h
t
>
i
right>i
right>i,那么
P
[
i
]
的
长
度
至
少
为
m
i
n
(
r
i
g
h
t
−
i
,
P
[
m
i
d
−
(
i
−
m
i
d
)
]
)
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 second
using 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预处理。
我们发现最后一个串 s u f f i x suffix suffix一定是原串的一个后缀,因此我们考虑枚举 s u f f i x suffix suffix的长度,找到最前面的一个 p r e f i x prefix prefix,使得 p r e f i x prefix prefix是 s u f f i x suffix suffix的反串,这一部分可以用KMP实现。
在找到 p r e f i x , s u f f i x prefix,suffix prefix,suffix以后,我们还需要找一个最优的 m i d d l e middle middle,它是一个长度为奇数的回文串,并包含在 [ p r e f f i x + 1 , s u f f i x − 1 ] [preffix+1,suffix-1] [preffix+1,suffix−1]之间,但我们直接求这里面的最长回文子串是不行的,因为其中的最长回文子串可能会与 p r e f f i x preffix preffix或 s u f f i x suffix suffix重合,因此考虑二分答案,则问题变成了判断 [ p r e f f i x + 1 + m i d , s u f f i x − 1 − m i d ] [preffix+1+mid,suffix-1-mid] [preffix+1+mid,suffix−1−mid]之间是否有最长回文子串长度大于 m i d mid mid。
这就变成了一个区间最长回文子串最大值,Manacher求出 P [ i ] P[i] P[i]之后, S T ST ST表预处理就能够 O ( 1 ) O(1) O(1)询问了。
总时间复杂度为 O ( n l g n ) 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 second
using 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
*/