https://loj.ac/problem/2033
题意:这道题是中文题。就是给你一个字符串,询问每一个前缀中有多少个不同子串。
做法:说到不同子串的数量,应该想到的是某种字符串算法。
这道题应该用后缀自动机解决。
首先一个字符串中不同子串的数量就是从空串起点
t
0
t_0
t0出发有多少条路径。
我们可以利用SAM的树形结构跑一个动态规划
d
p
[
u
]
=
1
+
∑
v
d
p
[
v
]
dp[u]=1+\sum_{v}dp[v]
dp[u]=1+∑vdp[v]
就可以了,但不过这道题这样写似乎很难维护。
我们就可以换一种方法维护。
另一种方法是利用后缀自动机的树形结构。每个节点对应的子串数量是
l
e
n
[
i
]
−
l
e
n
[
l
i
n
k
[
i
]
]
len[i]-len[link[i]]
len[i]−len[link[i]],对自动机所有节点求和即可。
这道题没加入一个字符就加上这个结点的值,不用管加入过程中
c
l
o
n
e
clone
clone的结点,你可以画一个图,发现他对答案的总和并不影响。
#include "bits/stdc++.h"
using namespace std;
typedef long long ll;
const int maxn = 100000 + 10;
const int inf = 0x3f3f3f3f;
struct state {
int len, link;
// int next[26];
unordered_map<int, int> next;
} st[maxn << 1];
int sz, last;
void init() {
st[0].len = 0;
st[0].link = -1;
sz++;
last = 0;
}
void extend(int c) {
int cur = sz++;
st[cur].len = st[last].len + 1;
int p = last;
while (p != -1 && !st[p].next[c]) {
st[p].next[c] = cur;
p = st[p].link;
}
if (p == -1) {
st[cur].link = 0;
} else {
int q = st[p].next[c];
if (st[p].len + 1 == st[q].len) {
st[cur].link = q;
} else {
int clone = sz++;
st[clone].len = st[p].len + 1;
// memcpy(st[clone].next, st[q].next, sizeof(st[q].next));
st[clone].next = st[q].next;
st[clone].link = st[q].link;
while (p != -1 && st[p].next[c] == q) {
st[p].next[c] = clone;
p = st[p].link;
}
st[q].link = st[cur].link = clone;
}
}
last = cur;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
int n, x;
ll ans = 0;
cin >> n;
init();
for (int i = 1; i <= n; i++) {
cin >> x;
extend(x);
ans += st[last].len - st[st[last].link].len;
cout << ans << endl;
}
return 0;
}