Manacher
按照惯例,先放上
m
a
n
a
c
h
e
r
manacher
manacher(其实是为了给自己看。。。
首先为了处理奇数串和偶数串的问题,我们可以给两个字符之间插入一些特殊字符。
设
p
[
i
]
p[i]
p[i]为以
i
i
i为中心最长回文串长度。
设
m
x
mx
mx为当前
i
+
p
[
i
]
i+p[i]
i+p[i]的最大值,
i
d
id
id为
m
x
mx
mx的
i
i
i。
假设当前加入一个新节点
i
i
i,如果
i
<
=
m
x
i<=mx
i<=mx,那么说明他处在以
i
d
id
id为中心的回文串中,此时必定有一个以
i
d
id
id为中心的对应节点,那么我们从这个位置直接继承
p
[
i
]
p[i]
p[i],然后暴力往外拓展。
否则直接暴力往外拓展。
考虑时间复杂度的证明。
加入不在镜像中,往外拓展的一定是之前没拓展过的。
加入在镜像中,我们考虑能扩展的情况肯定只有
也就是说只有
i
′
i'
i′拓展超过了
i
d
id
id的左端点时,
i
i
i才有可能会拓展,否则就会跟
i
′
i'
i′一模一样,我们注意到黄色那段是没有拓展过的。
也就是说,每次拓展只会拓展未拓展过的,所以时间复杂度是线性的。
PAM
也就是回文自动机,我们用一个节点表示一个回文串。
其实这个东西跟
A
C
AC
AC自动机类似,他有两颗字符树,一棵是代表偶数长度回文串的树,一棵是代表奇数长度回文串的树,一棵以
0
0
0作为根节点,长度为
0
0
0,一棵以
1
1
1为根节点,长度为
−
1
-1
−1,特殊的
0
0
0的
f
a
i
l
fail
fail为
1
1
1。
我们要维护的信息有:
l
e
n
[
i
]
len[i]
len[i]:
i
i
i这个节点所代表的回文串的长度。
f
a
i
l
[
i
]
fail[i]
fail[i]:
i
i
i这个节点所代表的回文串的最长后缀回文串。
c
n
t
[
i
]
cnt[i]
cnt[i]:
i
i
i这个节点所代表的回文串的出现次数。
c
h
[
i
]
[
c
]
ch[i][c]
ch[i][c]:
i
i
i这个节点往外加一个字符
c
c
c所产生的新回文串。
l
a
s
t
last
last:与后缀自动机的
l
a
s
t
last
last类似,表示上一个添加的字母所产生的回文串所代表的节点。
每次添加一个点我们直接在 l a s t last last跳 f a i l fail fail即可,具体过程跟 A C AC AC自动机是一样的。
那么如何证明时间复杂度呢。
首先根据
m
a
n
a
c
h
e
r
manacher
manacher的时间复杂度的证明,每次只有往外拓展时才会产生新的不同的回文串,而总拓展次数是线性的,所以回文自动机节点也是线性的。
然后我们考虑
f
a
i
l
fail
fail指针的移动为什么是线性的,每一个
f
a
i
l
fail
fail指针注意到他只会失配一次,而每一个节点也只会成功匹配一次,所以总的时间复杂度是线性的。
code:BZOJ3676: [Apio2014]回文串
#include <ctime>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
typedef long long LL;
LL _max(LL x, LL y) {return x > y ? x : y;}
int _min(int x, int y) {return x < y ? x : y;}
const int N = 300001;
int read() {
int s = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
return s * f;
}
struct PAM {
char ss[N];
int cnt, last, len[N], s[N], fail[N], son[26][N];
int new_node(int L) {
len[++cnt] = L;
return cnt;
}
int get_fail(int now, int n) {
while(ss[n - len[now] - 1] != ss[n])
now = fail[now];
return now;
}
void ins(int c, int i) {
int hh = get_fail(last, i);
if(!son[c][hh]) {
int now = new_node(len[hh] + 2);
fail[now] = son[c][get_fail(fail[hh], i)];
son[c][hh] = now;
} ++s[last = son[c][hh]];
}
void bt() {
cnt = -1; new_node(0), new_node(-1);
last = 0; fail[0] = 1; int u = strlen(ss + 1);
for(int i = 1; i <= u; i++) ins(ss[i] - 'a', i);
}
LL getmx() {
LL maxx = 0;
for(int i = cnt; i >= 1; i--) s[fail[i]] += s[i], maxx = _max(maxx, (LL)s[i] * len[i]);
return maxx;
}
} t;
int main() {
scanf("%s", t.ss + 1); t.ss[0] = -1;
t.bt();
printf("%lld\n", t.getmx());
return 0;
}