省选算法学习-回文自动机 && 回文树

前置知识

首先你得会manacher,并理解manacher为什么是对的(不用理解为什么它是$O(n)$,这个大概记住就好了,不过理解了更方便做$PAM$的题)

什么是回文自动机?

回文自动机(Palindrome Automaton),是一类有限状态自动机,能识别一个字符串的所有回文子串

它可简化构建出回文树

回文自动机的构造

网上资料很多,不拿出来一步步说了,说一下数组意义、放个板子

$len[u]$表示节点$u$代表的回文串的长度

$ch[u][c]$代表在$u$的回文串两端添加字符$c$得到的新回文串节点

$fail[u]$表示节点$u$的回文串的最长的回文后缀所在的节点

char s[300010];
int n;
//这个板子里面的num表示当前节点的回文串出现了几遍
namespace pam{
	int fail[300010],num[300010],len[300010],ch[300010][26],last,cnt;
	inline int newnode(int w){len[++cnt]=w;return cnt;}
	void init(){
		s[0]=-1;cnt=-1;fail[0]=1;last=0;
		newnode(0);newnode(-1);//插入两个根节点,设置fail
	}
	inline int getfail(int cur,int pos){
		while(s[pos-len[cur]-1]!=s[pos]) cur=fail[cur];//跳fail
		return cur;
	}
	void insert(int x){
		int c=(s[x]-'a'),cur=getfail(last,x);
		if(!ch[cur][c]){//新建节点
			int now=newnode(len[cur]+2);
			fail[now]=ch[getfail(fail[cur],x)][c];
			ch[cur][c]=now;
		}
		num[last=ch[cur][c]]++;
	}
}

几个容易写错的点:

fail初始化的时候,如果多组数据并且在newnode里面初始化信息,那么在init的时候记得把fail放到newnode后面

插入的时候函数传进去的是位置

newnode是len[cur]+2不是+1

一些拓展

节点上

首先显然可以统计这个节点的回文串出现次数

统计次数的时候还要加上$fail$树上子树内的所有节点的出现次数

对于一类回文串拥有某个和其$fail$树有关的性质的题目,可以记录一个$trans$,和跳$fail$一样跳,最后用$bfs$来做$dp$或者递推

关于求最小回文串分解

这个问题是问可以把一个字符串分解成最少多少个回文串

解决的方法:

考虑一个显然的$dp$:$dp[i]=dp[j-1]+1 (s[i...j]=palindrome)$

记录一个$anc[u]$

如果$len[u]-len[fail[u]]==len[fail[u]]-len[fail[fail[u]]]$,那么$anc[u]=anc[fail[u]]$

否则$anc[u]=u$

对于$u$的所有跳上去的$anc$集合$S$,我们发现,这个集合中的元素构成一个等差数列,相邻的两项差代表一种从当前点前面递推到当前点的回文串长度

对于每个回文树节点记录$tmp[u]=min(tmp[i-len[anc[u]],tmp[fail[u]])$,然后用这个$tmp[u]+1$来更新当前节点的$dp$,然后$u=fail[anc[u]]$往上跳,直到到达根

证明网上有论文,这里放个代码

inline void insert(int x){
	int c=s[x]-'a',cur=getfail(last,x);
	val[x]=1e9;
	if(!ch[cur][c]){
		int now=newnode(len[cur]+2);
		fail[now]=ch[getfail(fail[cur],x)][c];
		ch[cur][c]=now;
		anc[now]=((fail[now]>1&&len[now]-len[fail[now]]==len[fail[now]]-len[fail[fail[now]]])?anc[fail[now]]:now);
	}
	last=ch[cur][c];
	for(cur=ch[cur][c];cur>1;cur=fail[anc[cur]]){
		tval[cur]=val[x-len[anc[cur]]];
		tpos[cur]=x-len[anc[cur]];
		if(anc[cur]!=cur&&tval[fail[cur]]<tval[cur]) tval[cur]=tval[fail[cur]],tpos[cur]=tpos[fail[cur]];
		if(val[x]>tval[cur]+1) val[x]=tval[cur]+1,pos[x]=tpos[cur];
	}
}
posted @ 2018-12-20 13:51  dedicatus545  阅读(190)  评论(0编辑  收藏  举报