PAM 入门

前后端插入

题目链接:https://vjudge.net/problem/HDU-5421

注意到回文后缀也是回文前缀,所以大体上没有变化。

只是要维护最长回文前缀和后缀,当最长回文前/后缀等于原串时,记得更新另外一个。

时间复杂度:$O(n)$ 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#include<cstdio>
#include<cstring>
#include<iostream>
#include<string>
using namespace std;
typedef long long LL;
const int N = 1e5 + 5;
int len[N], fail[N], go[N][26], llast, rlast, pam_cnt;
int dep[N];
LL ans;

char s[N * 2];
int lenl, lenr, n;
void clgo(int x){memset(go[x], 0, sizeof(go[x]));}
void init(){
fail[0] = pam_cnt = 1;
len[1] = -1;
llast = rlast = 0;
ans = 0;
clgo(0);
clgo(1);
}
int getLfail(int p){
while(lenl + len[p] + 1 > lenr || s[lenl + len[p] + 1] != s[lenl]) p = fail[p];
return p;
}
int getRfail(int p){
while(lenr - len[p] - 1 < lenl || s[lenr - len[p] - 1] != s[lenr]) p = fail[p];
return p;
}
void extend(int type){
int p, c, *last;
if(type == 0) p = getLfail(llast), c = s[lenl] - 'a', last = &llast;
else p = getRfail(rlast), c = s[lenr] - 'a', last = &rlast;
if(!go[p][c]){
int q = ++pam_cnt, now = p;
clgo(q);
len[q] = len[p] + 2;
if(type == 0) p = getLfail(fail[p]);
else p = getRfail(fail[p]);
fail[q] = go[p][c];
go[now][c] = q;
dep[q] = dep[fail[q]] + 1;
if(lenr - lenl + 1 == len[q]) llast = rlast = q;
else *last = q;
}
else *last = go[p][c];
ans += dep[*last];
}

int main(){
while(cin >> n){
init();
lenl = n + 1;
lenr = n;
for(int i = 1; i <= n; i++){
int type;
cin >> type;
if(type <= 2){
string x; cin >> x;
if(type == 1) s[--lenl] = x[0], extend(0);
else s[++lenr] = x[0], extend(1);
}
else if(type == 3) cout << pam_cnt - 1 << "\n";
else cout << ans << "\n";
}
}
return 0;
}

今天突然意识到一个很严重的问题,双端插入回文串的时间复杂度为啥对呢?原因是最长回文前后缀相等的那个特判,如果不加分析,会破坏均摊分析,所以现在来分析一下双端插入回文串的时间复杂度:

不妨认为插入到后面,插入后左边的最长回文前缀为 $L$ ,右边为 $R$ ,显然 $L$ 是 $R$ 的最长回文真前缀,否则与插入前 $L$ 是最长回文前缀矛盾,所以在回文树上,$L$ 和 $R$ 的深度差恰好为 $1$ ,所以这个操作只会让势能增加 $1$ ,不会破坏均摊分析。

证毕。

去均摊化

可以用于解决后端插入/删除 、 Trie 上 PAM 、可持久化 PAM 的问题。

详情请见 KMP 和 PAM 的可持久化。

在此不再赘述。

前后端插入/删除

PS :同样可以用同样的方法可持久化。