前言
给我也来一个
玩法
为啥人家的博客都要放个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[i−1−len[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[i−1−len[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
n∗siz与
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;
}