PAM学习笔记

前言

给我也来一个

玩法

为啥人家的博客都要放个manachar…感觉这两个没啥关系啊qwq

每个状态 x x x代表了一个长度为 l e n [ x ] len[x] len[x]的回文串
我们保证他代表的这个回文串是他能代表的里面最大的那个

这是一个自动机
不是一个树qwq…,他的fail才是一个树
s o n [ x ] [ i ] son[x][i] son[x][i]表示在状态 x x x后加一个字符 i i i能转移到哪个回文串的状态
l e n [ x ] len[x] len[x]表示这个状态 x x x代表了一个长度为 l e n [ x ] len[x] len[x]的回文串
c n t [ x ] cnt[x] cnt[x]表示这个状态 x x x代表的回文串出现了多少次
f a i l [ x ] fail[x] fail[x]表示这个状态代表的回文串的最长回文后缀所在的状态是什么
记录一个 l a s t last last表示上一个位置插入进去后的状态是哪一个

初始状态给两个根 0 , 1 0,1 0,1
分别表示偶数串的树根与奇数串的树根
f a i l [ 0 ] = 1 , l e n [ 1 ] = − 1 fail[0]=1,len[1]=-1 fail[0]=1,len[1]=1

考虑怎么建树
过程其实跟 A C AC AC自动机差不多
插入一个位置 i i i,设其字符为 w w w
l a s t last last f a i l fail fail指针直到 S [ i − 1 − l e n [ l a s t ] ] = S [ i ] S[i-1-len[last]]=S[i] S[i1len[last]]=S[i]
那么我们观察 s o n [ l a s t ] [ w ] son[last][w] son[last][w]是否有转移
如果有,直接把 l a s t = s o n [ l a s t ] [ w ] last=son[last][w] last=son[last][w],并将 s o n [ l a s t ] [ w ] son[last][w] son[last][w] c n t + + cnt++ cnt++
否则我们找到了一个全新的回文串
新建节点,其长度为 l e n [ l a s t ] + 2 len[last]+2 len[last]+2
再跳 l a s t last last f a i l fail fail指针直到 S [ i − 1 − l e n [ l a s t ] ] = S [ i ] S[i-1-len[last]]=S[i] S[i1len[last]]=S[i]
此时新节点的 f a i l fail fail就指向了 s o n [ l a s t ] [ w ] son[last][w] son[last][w]

然后自动机就建完了

注意我们每次只是在完全相等于某个回文串的状态处打了 c n t + 1 cnt+1 cnt+1的标记
所以想要得到真正的 c n t cnt cnt,你还需要把包含他的位置给加上
那么从大往小扫节点,每次给他的 f a i l fail fail累加他自己的 c n t cnt cnt

不基于势能分析

需要势能分析的显然就是类似 a c ac ac机那样往上跳 f a i l fail fail,我们只需要处理出 l i n k i link_i linki表示跳 f a i l fail fail到第一个满足前驱是 i i i的回文后缀是什么,于是就可以 O ( 1 ) O(1) O(1)找了
可以直接 c o p y copy copy f a i l fail fail l i n k link link数组过来,再将 f a i l fail fail这个回文后缀连同他的前驱插进去,于是字符集较小的时候可以暴力,字符集较大的时候就需要用可持久化线段树维护可持久化数组,复杂度分别为 n ∗ s i z n*siz nsiz n log ⁡ s i z n\log siz nlogsiz

板子

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、付费专栏及课程。

余额充值