LibreOJ 6071 「2017 山东一轮集训 Day5」字符串

首先题目中「本质不同」指的是表示方式本质不同,即每个用来拼接的子串分别本质不同,但 \(t\) 并不一定要本质不同。

\(n = 1\) 时,显然在 SAM 上把每个状态包含的子串数量求和即可。
当然,也可以在 DAG 上 DP。

\(n > 1\) 时,广义 SAM 貌似搞不来这种东西。
考虑照样在 DAG 上 DP。那么我们需要的 DAG 要类似一个分层图的形式,若在本层失配则连向之后最近的一个能匹配的结点。

这个东西与序列自动机十分类似。实际上,表示方式就可以看做某种子序列的定义。
考虑建出 \(n\) 个 SAM,对于第 \(i\) 个 SAM 上的状态 \(u\),若不存在字符 \(c\) 的出边,就寻找 \(i\) 之后的 SAM 中第一个有 \(c\) 出边的根,从 \(u\) 连一条出边到那个根的出边指向的状态。

这样子,我们便能判定一个 \(t\) 是否能被接受,或者说构造出一个能够按题目中方式接受字符串的自动机了。
并且,每条从第 \(1\) 个 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
64
65
66
67
68
69
70
71
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 1e6;
const int mod = 1e9 + 7;
int n,m;
char s[N + 5];
int rt[N + 5],nxt[26];
namespace SAM
{
struct node
{
int ch[26];
int fa,len;
} sam[(N << 1) + 5];
int las,tot;
inline void insert(int x,int rt)
{
int cur = las,p = ++tot;
sam[p].len = sam[cur].len + 1;
for(;cur && !sam[cur].ch[x];cur = sam[cur].fa)
sam[cur].ch[x] = p;
if(!cur)
sam[p].fa = rt;
else
{
int q = sam[cur].ch[x];
if(sam[cur].len + 1 == sam[q].len)
sam[p].fa = q;
else
{
int nxt = ++tot;
sam[nxt] = sam[q],sam[nxt].len = sam[cur].len + 1,sam[p].fa = sam[q].fa = nxt;
for(;cur && sam[cur].ch[x] == q;cur = sam[cur].fa)
sam[cur].ch[x] = nxt;
}
}
las = p;
}
}
int f[(N << 1) + 5];
int dfs(int p)
{
if(~f[p])
return f[p];
int ret = 1;
for(register int i = 0;i < 26;++i)
if(SAM::sam[p].ch[i])
ret = (ret + dfs(SAM::sam[p].ch[i])) % mod;
return f[p] = ret;
}
int main()
{
memset(f,-1,sizeof f),scanf("%d",&n);
for(register int i = 1;i <= n;++i)
{
scanf("%s",s + 1),m = strlen(s + 1),rt[i] = SAM::las = ++SAM::tot;
for(register int j = 1;j <= m;++j)
SAM::insert(s[j] - 'a',rt[i]);
}
rt[n + 1] = SAM::tot + 1;
for(register int i = n;i;--i)
{
for(register int j = rt[i];j < rt[i + 1];++j)
for(register int k = 0;k < 26;++k)
!SAM::sam[j].ch[k] && (SAM::sam[j].ch[k] = nxt[k]);
for(register int j = 0;j < 26;++j)
SAM::sam[rt[i]].ch[j] && (nxt[j] = SAM::sam[rt[i]].ch[j]);
}
printf("%d\n",dfs(1));
}