PAM学习笔记

本文介绍了一种基于Manachar算法的自动机实现方法,用于高效处理字符串中的回文串匹配问题。通过构建特殊的自动机结构,可以在常数时间内找到字符串中的所有回文串,并统计它们出现的次数。

前言

给我也来一个

玩法

为啥人家的博客都要放个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
lastlastlastfailfailfail指针直到S[i−1−len[last]]=S[i]S[i-1-len[last]]=S[i]S[i1len[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
再跳lastlastlastfailfailfail指针直到S[i−1−len[last]]=S[i]S[i-1-len[last]]=S[i]S[i1len[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)找了
可以直接copycopycopyfailfailfaillinklinklink数组过来,再将failfailfail这个回文后缀连同他的前驱插进去,于是字符集较小的时候可以暴力,字符集较大的时候就需要用可持久化线段树维护可持久化数组,复杂度分别为 n∗sizn*siznsiznlog⁡sizn\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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值