【模板】【回文自动机】

洛谷P5496

题目

求回文子串个数

题解

回文自动机是个好东西,可惜我不会(逃


回文自动机

不同于其他学过的自动机,回文自动机是两棵树组成的
一棵奇数树,一棵偶数树,可以理解?

内容

同样,每个节点代表一种状态。
基础维护的东西有:
l e n i 当 前 节 点 对 应 的 状 态 长 s u m i 当 前 节 点 对 应 状 态 的 出 现 次 数 f a i l i   当 前 节 点 对 应 状 态 的   最 大 后 缀 回 文 串 对 应 节 点 c h i , j 表 示 状 态 转 移 ( 和 其 他 自 动 机 相 同 ) len_i当前节点对应的状态长 \\ sum_i当前节点对应状态的出现次数\\ fail_i \ 当前节点对应状态 的\ 最大后缀回文串对应节点\\ ch_{i,j}表示状态转移(和其他自动机相同) lenisumifaili  chi,j

初始化

偶 树 的 根 节 点 是 0 , l e n 0 = 0     , f a i l 0 = 0 ( 偶 数 偶 树 。 。 。 ) 奇 树 的 根 节 点 是 1 , l e n 1 = − 1 , f a i l 1 = 1 偶树的根节点是0,len_0=0\ \ \ ,fail_0=0(偶数偶树。。。)\\ 奇树的根节点是1,len_1=-1,fail_1=1 0len0=0   ,fail0=01len1=1,fail1=1

基础操作:

get_fail(x,pos)

x代表节点,pos代表字符串的一个位置
找到pos最大能与 x哪个后缀回文串 组成新的回文串

int get_fail(int x,int pos)
{
	while(s[pos-len[x]-1]!=s[pos])x=fail[x];//手推一下很容易理解
	return x;
}
加点insert(pos)

1.之前有这个状态就不用新加了
2.新节点是旧节点 l e n + 2 len+2 len+2,因为是回文(旧节点就是新节点要连的地方)
3.新节点的最大后缀回文,就是旧节点的最大后缀回文?不,应为 旧节点的最大后缀回文串的子串 且能与 新节点 能组成新回文
4.sum累计
5.pre更新,用来之后加入新节点

int make_new(int l)
{
	++cnt;
	len[cnt]=l;
	return cnt;//其实这个都不用另开函数
}
void insert(int pos)
{
	int cur=get_fail(pre,pos),st=s[pos]-'a';
	if(!ch[cur][st])//原森林没有这个点就要新建
	{
		int now=make_new(len[cur]+2);
		fail[now]=ch[get_fail(fail[cur],pos)][st];//3.
		sum[now]=sum[fail[now]]+1;
		ch[cur][st]=now;
	}
	pre=ch[cur][st];
}

本人刚学,自己总结,以后有空补补(或者就弃坑了)

//P5496的丢人代码
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+10;
int n,pre;
char s[N];

int fail[N],len[N],sum[N];
int ch[N][30],cnt=1;
void init()
{
	len[0]=0;len[1]=-1;
	fail[0]=1;fail[1]=0;
	pre=0;
}
int make_new(int l)
{
	++cnt;
	len[cnt]=l;
	return cnt;
}
int get_fail(int x,int pos)
{
	while(s[pos-len[x]-1]!=s[pos])x=fail[x];
	return x;
}
void insert(int pos)
{
	int cur=get_fail(pre,pos),st=s[pos]-'a';
	if(!ch[cur][st])
	{
		int now=make_new(len[cur]+2);
		fail[now]=ch[get_fail(fail[cur],pos)][st];
		sum[now]=sum[fail[now]]+1;
		ch[cur][st]=now;
	}
	pre=ch[cur][st];
}
int main()
{
	int last=0;
	init();
	scanf("%s",s+1);n=strlen(s+1);
	for(int i=1;s[i];i++)
	{
		s[i]=(s[i]-97+last)%26+97;
		insert(i);
		printf("%d ",last=sum[pre]);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值