前言
给我也来一个
玩法
为啥人家的博客都要放个manachar…感觉这两个没啥关系啊qwq
每个状态xxx代表了一个长度为len[x]len[x]len[x]的回文串
我们保证他代表的这个回文串是他能代表的里面最大的那个
这是一个自动机
不是一个树qwq…,他的fail才是一个树
son[x][i]son[x][i]son[x][i]表示在状态xxx后加一个字符iii能转移到哪个回文串的状态
len[x]len[x]len[x]表示这个状态xxx代表了一个长度为len[x]len[x]len[x]的回文串
cnt[x]cnt[x]cnt[x]表示这个状态xxx代表的回文串出现了多少次
fail[x]fail[x]fail[x]表示这个状态代表的回文串的最长回文后缀所在的状态是什么
记录一个lastlastlast表示上一个位置插入进去后的状态是哪一个
初始状态给两个根0,10,10,1
分别表示偶数串的树根与奇数串的树根
fail[0]=1,len[1]=−1fail[0]=1,len[1]=-1fail[0]=1,len[1]=−1
考虑怎么建树
过程其实跟ACACAC自动机差不多
插入一个位置iii,设其字符为www
跳lastlastlast的failfailfail指针直到S[i−1−len[last]]=S[i]S[i-1-len[last]]=S[i]S[i−1−len[last]]=S[i]
那么我们观察son[last][w]son[last][w]son[last][w]是否有转移
如果有,直接把last=son[last][w]last=son[last][w]last=son[last][w],并将son[last][w]son[last][w]son[last][w]的cnt++cnt++cnt++
否则我们找到了一个全新的回文串
新建节点,其长度为len[last]+2len[last]+2len[last]+2
再跳lastlastlast的failfailfail指针直到S[i−1−len[last]]=S[i]S[i-1-len[last]]=S[i]S[i−1−len[last]]=S[i]
此时新节点的failfailfail就指向了son[last][w]son[last][w]son[last][w]
然后自动机就建完了
注意我们每次只是在完全相等于某个回文串的状态处打了cnt+1cnt+1cnt+1的标记
所以想要得到真正的cntcntcnt,你还需要把包含他的位置给加上
那么从大往小扫节点,每次给他的failfailfail累加他自己的cntcntcnt
不基于势能分析
需要势能分析的显然就是类似acacac机那样往上跳failfailfail,我们只需要处理出linkilink_ilinki表示跳failfailfail到第一个满足前驱是iii的回文后缀是什么,于是就可以O(1)O(1)O(1)找了
可以直接copycopycopy他failfailfail的linklinklink数组过来,再将failfailfail这个回文后缀连同他的前驱插进去,于是字符集较小的时候可以暴力,字符集较大的时候就需要用可持久化线段树维护可持久化数组,复杂度分别为 n∗sizn*sizn∗siz与nlogsizn\log siznlogsiz
板子
bzoj3676: [Apio2014]回文串
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<ctime>
#include<map>
#include<bitset>
#include<set>
#define LL long long
#define mp(x,y) make_pair(x,y)
#define pll pair<long long,long long>
#define pii pair<int,int>
using namespace std;
inline int read()
{
int f=1,x=0;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int stack[20];
inline void write(LL x)
{
if(x<0){putchar('-');x=-x;}
if(!x){putchar('0');return;}
int top=0;
while(x)stack[++top]=x%10,x/=10;
while(top)putchar(stack[top--]+'0');
}
inline void pr1(int x){write(x);putchar(' ');}
inline void pr2(LL x){write(x);putchar('\n');}
const int MAXN=300005;
char ch[MAXN];
int S[MAXN];
struct PAM
{
int son[MAXN][27],fail[MAXN],cnt[MAXN],len[MAXN],last,tot;
void init()
{
fail[0]=1;tot=2;
memset(son[0],0,sizeof(son[0]));
memset(son[1],0,sizeof(son[1]));
last=0;len[1]=-1;
}
int newnode(int L)
{
tot++;
memset(son[tot],0,sizeof(son[tot]));
len[tot]=L;
return tot;
}
void add(int x)
{
int p=last,w=S[x],np;
while(S[x-1-len[p]]!=S[x])p=fail[p];
if(!son[p][w])
{
np=newnode(len[p]+2);
int t=fail[p];
while(S[x-1-len[t]]!=S[x])t=fail[t];
fail[np]=son[t][S[x]];
son[p][w]=np;
}
else np=son[p][w];
cnt[last=np]++;
}
}tr;
int main()
{
scanf("%s",ch+1);int len=strlen(ch+1);
for(int i=1;i<=len;i++)S[i]=ch[i]-'a'+1;
tr.init();
for(int i=1;i<=len;i++)tr.add(i);
for(int i=tr.tot;i>=1;i--)tr.cnt[tr.fail[i]]+=tr.cnt[i];
LL ans=0;
for(int i=1;i<=tr.tot;i++)
ans=max(ans,1LL*tr.len[i]*tr.cnt[i]);
pr2(ans);
return 0;
}
本文介绍了一种基于Manachar算法的自动机实现方法,用于高效处理字符串中的回文串匹配问题。通过构建特殊的自动机结构,可以在常数时间内找到字符串中的所有回文串,并统计它们出现的次数。
153

被折叠的 条评论
为什么被折叠?



