P3804

【模板】后缀自动机 (SAM)

首先我们知道 SAM 是个自动机,字符串 $s$ 的 SAM 满足如下几个条件:

  1. 有一个初始状态 $t_0$。

  2. 从 $t_0$ 开始到某个终止状态结束的路径可以表示字符串 $s$ 的一个后缀。

  3. SAM 包含 $s$ 的所有后缀信息。

  4. 满足包含所有后缀信息的状态下具有最少的状态数(即最小性)。

首先我们引入字符串 $s$ 的子串 $t$ 的 $endpos(t)$,其中 $endpos(t)$ 是一个集合,它表示所有 $t$ 出现的结束位置。

然后我们可以发现,对于某些字符串,比如 $t_1$ 和 $t_2$,$endpos(t_1)$ 和 $endpos(t_2)$ 有可能是相同的。我们把 $t_1$ 和 $t_2$ 归于一个等价类。

然后我们发现一个等价类显然可以对应上一个 SAM 中的状态,对于某个状态,它是可以属于多个后缀的(到达终止状态的路径),这几个后缀有着共同的前缀,显然这个子串又有一堆后缀,一部分后缀与其属于一个等价类,一部分后缀不属于。

显然对于一个状态的这一部分后缀,若属于同一个等价类,它们的长度显然是连续的。

算了先滚去背代码再说吧。。。

代码:

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
#include<iostream>
#include<cstdio>
#include<map>
#include<cstring>
#define ll int
using namespace std;

const ll N=1e6;

struct State{ll len,link,d;ll nxt[27];}st[N*2+5];
ll n,sz,last,ans,tot;
ll ver[N*2+5],nxt[N*2+5],head[N*2+5];
char s[N+5];

inline void SAM_Init() {sz=1;st[sz].len=0;st[sz].link=0;last=sz;}

inline void SAM_Extend(ll c) {
ll cur=++sz,p=last;st[cur].len=st[last].len+1;st[sz].d=1;
for(;p&&!st[p].nxt[c];p=st[p].link) st[p].nxt[c]=cur;
if(!p) {st[cur].link=1;}
else {
ll q=st[p].nxt[c];
if(st[p].len+1==st[q].len) {st[cur].link=q;}
else {
ll clone=++sz;
st[clone]=st[q];
st[clone].len=st[p].len+1;st[clone].d=0;
st[q].link=st[cur].link=clone;
for(;p&&st[p].nxt[c]==q;p=st[p].link) st[p].nxt[c]=clone;
}
}
last=cur;
}

inline void Addedge(ll u,ll v) {
ver[++tot]=v;nxt[tot]=head[u];head[u]=tot;
}

inline void DP(ll p) {
for(ll i=head[p];i;i=nxt[i]) {
DP(ver[i]);st[p].d+=st[ver[i]].d;
}
if(st[p].d>1) {ans=max(ans,st[p].d*st[p].len);}
}

inline void write(ll x) {
static char buf[22];static ll len=-1;
if(x>=0) {do{buf[++len]=x%10+48;x/=10;}while(x);}
else {putchar(45);do{buf[++len]=-(x%10)+48;x/=10;}while(x);}
while(len>=0) putchar(buf[len--]);
}

int main() {

SAM_Init();scanf("%s",s);n=strlen(s);
for(ll i=0;i<n;i++) {SAM_Extend(s[i]-'a');}
for(ll i=2;i<=sz;i++) {Addedge(st[i].link,i);}
DP(1);

write(ans);

return 0;
}