SPOJ 8093 JZPGYZ - Sevenk Love Oimaster

题目描述

https://www.luogu.com.cn/problem/SP8093

简要题意:给定 $n$ 个模板串和 $m$ 个查询串,对于每个查询串查询它在几个模板串中作为子串出现

$len\le 3.6\times 10^5$

Solution

我们对于所有模板串建广义 $SAM$,然后用线段树合并求出 $SAM$ 上每个节点是多少个模板串的子串

然后对于每个询问串,只需要将其放到 $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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#include <iostream>
#include <cstring>
#include <vector>
#define maxn 100010
#define Maxn 200010
#define maxm 360010
#define ll long long
using namespace std;

int n, m, pos[maxn];

char s[maxn], c[maxm];

struct SAM {
struct node {
int l, L, nxt[26], cnt;
} T[Maxn]; int f[Maxn], top, rt, last;
void init() {
rt = last = top = 1;
T[rt].L = T[rt].l = f[rt] = 0;
}

int extend(int p, int ch) {
if (T[p].nxt[ch]) {
int q = T[p].nxt[ch], nq;
if (T[q].L - 1 == T[p].L) return q;
nq = ++top; T[nq].L = T[p].L + 1; f[nq] = f[q];
for (int i = 0; i < 26; ++i) T[nq].nxt[i] = T[q].nxt[i];
while (p && T[p].nxt[ch] == q) T[p].nxt[ch] = nq, p = f[p];
f[q] = nq; return nq;
}
int np = ++top; T[np].L = T[p].L + 1;
while (p && !T[p].nxt[ch]) T[p].nxt[ch] = np, p = f[p];
if (!p) return f[np] = rt, np;
int q = T[p].nxt[ch];
if (T[q].L - 1 == T[p].L) f[np] = q;
else {
int nq = ++top; T[nq].L = T[p].L + 1; f[nq] = f[q];
for (int i = 0; i < 26; ++i) T[nq].nxt[i] = T[q].nxt[i];
while (p && T[p].nxt[ch] == q) T[p].nxt[ch] = nq, p = f[p];
f[np] = f[q] = nq;
}
return np;
}

void insert(char *s) {
int l = strlen(s), p = rt;
for (int i = 0; i < l; ++i) p = extend(p, s[i] - 'a');
}
} SAM;

struct SegmentTree {
#define lc T[i].ch[0]
#define rc T[i].ch[1]
#define Lc T[j].ch[0]
#define Rc T[j].ch[1]
struct node {
int v, ch[2];
} T[Maxn * 20]; int rt[Maxn], top;
inline void maintain(int i) { T[i].v = T[lc].v + T[rc].v; }

void update(int &i, int l, int r, int k, int v) {
if (!i) i = ++top;
if (l == r) return T[i].v = v, void();
int m = l + r >> 1;
if (k <= m) update(lc, l, m, k, v);
else update(rc, m + 1, r, k, v);
maintain(i);
}

void merge(int &i, int j, int l, int r) {
if (!i) return i = j, void();
if (!j) return ;
if (l == r) return T[i].v |= T[j].v, void();
int m = l + r >> 1;
merge(lc, Lc, l, m); merge(rc, Rc, m + 1, r);
maintain(i);
}
} Seg;

struct Edge {
int to, next;
} e[Maxn]; int head[Maxn], c1;
inline void add_edge(int u, int v) {
e[c1].to = v; e[c1].next = head[u]; head[u] = c1++;
}

vector<int> A[Maxn];
void solve(int k, int l, int r) {
int p = SAM.rt;
for (int i = l; i <= r; ++i) {
p = SAM.T[p].nxt[s[i] - 'a'];
A[p].push_back(k);
}
}

void dfs(int u) {
for (auto t : A[u]) Seg.update(Seg.rt[u], 1, n, t, 1);
for (int i = head[u]; ~i; i = e[i].next) {
int v = e[i].to; dfs(v);
Seg.merge(Seg.rt[u], Seg.rt[v], 1, n);
} SAM.T[u].cnt = Seg.T[Seg.rt[u]].v;
}

int match(char *s) {
int l = strlen(s), p = SAM.rt;
for (int i = 0; i < l; ++i) p = SAM.T[p].nxt[s[i] - 'a'];
return SAM.T[p].cnt;
}

int main() { fill(head, head + Maxn, -1);
ios::sync_with_stdio(false);
cin.tie(nullptr); cout.tie(nullptr);

cin >> n >> m; SAM.init();
for (int i = 1; i <= n; ++i) {
cin >> s + pos[i];
SAM.insert(s + pos[i]);
pos[i + 1] = pos[i] + strlen(s + pos[i]);
}
for (int i = 1; i <= n; ++i) solve(i, pos[i], pos[i + 1] - 1);
for (int i = 2; i <= SAM.top; ++i) add_edge(SAM.f[i], i); dfs(SAM.rt);
for (int i = 1; i <= m; ++i) cin >> c, cout << match(c) << "\n";
return 0;
}