回文树/回文自动机/PAM总结——题解 P3649 【[APIO2014]回文串】
FREEH
2018-08-16 20:38:29
### 【题目】
![题目](https://cdn.luogu.com.cn/upload/pic/29011.png)
### 【分析】
- 回文树裸题。
### 【算法简介】
- 回文树,又称回文自动机,简称PAM。
- 作用:线性的时间复杂度求一个字符串里的回文串的个数,也可以通过记录多一些信息来实现更多的功能。
### 【算法步骤】
- 顾名思义,“回文自动机”与[AC自动机](https://12349.blog.luogu.org/ac-zi-dong-ji-zong-jie)有非常多的联系:
- 同样使用失配指针节约时间;
- 同样使用Trie树结构。
- 定义:len[i]表示第i个结点的回文串长度,fail[i]表示第i个结点的失配指针,son[i][c]表示结点i的字符c边指向的结点。
- PAM的有2个根,一个根代表偶长度的回文串(以下称为偶根),另一个代表奇长度的回文串(以下称为奇根)。特别的,定义偶根长度为0,fail指向自己,奇根的长度为-1(这里十分玄妙),fail指向自己。
- 与AC自动机相似,枚举字符串的每一位,求出最早到达的加上自己这一位后成为回文串的结点,如果不行就跑向失配指针,然后把插入回文自动机(如图):
- 自己作为找到的结点的儿子c。
- len是上一次的len+2;
- fail是找到的结点的fail的儿子。
- 累计数量。
![图1](https://cdn.luogu.com.cn/upload/pic/29010.png)
- 最终的答案就是所有结点的累计数量*长度的MAX。
### 【拓展】
- 累计数量的步骤可以像AC自动机一样,改为或添加各种玄妙的操作。
### 【参考程序】
```cpp
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
typedef long long LL;
#define int LL
struct PAM{
int len,fail,son[30];
}Ptree[300005];
int tot,last,len,l;
int cnt[300005];
char st[300005];
void Init()
{
Ptree[0].len=0;
Ptree[1].len=-1;
Ptree[0].fail=1;
tot=1;
last=0;
}
int Get(int now)
{
for (;st[l]!=st[l-Ptree[now].len-1];now=Ptree[now].fail);
return now;
}
void Add(int ch)
{
int u=Get(last);
if (Ptree[u].son[ch]==0)
{
int v=++tot;
Ptree[v].len=Ptree[u].len+2;
Ptree[v].fail=Ptree[Get(Ptree[u].fail)].son[ch];
Ptree[u].son[ch]=tot;
}
last=Ptree[u].son[ch];
cnt[last]++;
}
signed main()
{
scanf("%s",st+1);
len=strlen(st+1);
Init();
for (l=1;l<=len;l++)
Add(st[l]-'a');
int ans=0;
for (int i=tot;i>1;i--)
{
cnt[Ptree[i].fail]+=cnt[i];
ans=max(ans,cnt[i]*Ptree[i].len);
}
cout<<ans;
return 0;
}
```