回文自动机学习心得
回文自动机能够解决大部分的回文子串问题,其关键在于通过原字符串建立两棵树来存储原字符串中所有的回文子串。
要点:
1.回文自动机是一颗由两棵树构建的森林,该森林储存了原字符串所有回文子串的信息。一颗储存奇数长度回文子串,一颗储存偶数长度。
2.在回文自动中,数上的节点(一个非负整数)表示一个回文子串。通常保存了该串的长度等信息。
3.回文自动机中有两个最重要的“指针”一个叫next,一个叫fail,任何回文子串的fail指针有且仅指向一个对象,故用fail[i]来存储i节点所代表的回文子串指向的回文子串;任意一个回文子串的next指针可能有多个对象,也可能一个也没有,故用邻接矩阵next[i][]的方式存储。
4.fail与next的含义
fail[i]=j:节点j所代表的回文子串是节点i所代表回文子串的“最大回文后缀”
(最大回文后缀:从字符串最右端算起,该字符串的最大回文真子串,例如fail[“abc”]=“c”,fail[“aaa”]=“aa”,fail[“ababa”]=“aba”)
next[i][‘x’]:节点i代表的回文子串s 在两端添加字符’x’后行程的新回文串’xsx’是否是原串的回文子串。
通过这两个指针 自动机上的所有回文子串联系了起来,用于求解各类回文子串问题。
模板注释(模板原作者)
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
const int N=26;
struct Palindromic_Tree
{
int next[maxn][N];
int fail[maxn];
int cnt[maxn];//第i个节点表示的回文串出现的次数,最后要调用count函数完成计算
int num[maxn];//以节点i表示的最长回文串的最右端点为回文串结尾的回文串个数
int len[maxn];//节点i表示的回文串的长度
int S[maxn];//存放添加的字符
int last;
int n;//字符数组指针
int p;//节点指针
int newnode(int l)
{
memset(next[p],0,sizeof next[p]);
cnt[p]=0;
num[p]=0;
len[p]=l;
return p++;
}
void init()
{
p=0;
newnode(0);
newnode(-1);
last=0;
n=0;
S[n]=-1;
fail[0]=1;
}
int get_fail(int x)
{
while(S[n-len[x]-1]!=S[n])//如果
x=fail[x];
return x;
}
void add(int c)
{
c-='0';
S[++n]=c;
int cur=get_fail(last);
if(!next[cur][c])//如果这个回文串没有出现过,说明出现了一个新的本质不同的回文串
{
int now=newnode(len[cur]+2);
fail[now]=next[get_fail(fail[cur])][c];
next[cur][c]=now;
num[now]=num[fail[now]]+1;
}
last=next[cur][c];
cnt[last]++;
}
void count()
{
for(int i=p-1;i>=0;i--)
cnt[fail[i]]+=cnt[i];
}
}pam;
char s[maxn];
int main()
{
pam.init();
scanf("%s",s);
int n=strlen(s);
for(int i=0;i<n;i++)
{
pam.add(s[i]);
}
}