题面:https://ac.nowcoder.com/acm/contest/874/I
博客明天补,准备打cf,先贴代码。
sam:听说你强制在线?好巧哦我就是在线的。
5.5日:
咕咕咕。
现场不敢开不会开,最后现场0队通过。
回来看了一下牛客当时唯一的ac代码,发现卧槽这是个板子题。
然后大家看到第一想法都是“可持久化后缀自动机”hhhh
贴一句葫芦爷的话吧:“ 只求right集合大小的话,直接拓扑排序(计数排序)然后dp。要求每个点的right集合具体有什么的话,一般线段树合并 或者 dfs序主席树。”
把right集用线段树维护,建广义sam时继续一下有哪些串路过了当前right集,之后拓扑排序,从后往前跑个线段树合并,就可以求出维护了所有串贡献的right集-线段树version。
线段树用动态开点来优化。
ac代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 3e5 + 5;
int n, q;
string s, t;
int sum[maxn * 60], ls[maxn * 60], rs[maxn * 60];
int tot;
void update(int &i, int l, int r, int pos) {
if (!i) {
i = ++tot;
}
++sum[i];
if (l == r) {
return;
}
int mid = (l + r) >> 1;
if (pos <= mid) {
update(ls[i], l, mid, pos);
} else {
update(rs[i], mid + 1, r, pos);
}
}
int merge(int x, int y) {
if (!x || !y) {
return x + y;
}
int z = ++tot;
sum[z] = sum[x] + sum[y];
ls[z] = merge(ls[x], ls[y]);
rs[z] = merge(rs[x], rs[y]);
return z;
}
int query(int i, int l, int r, int L, int R) {
if ((l >= L && r <= R) || !i) {
return sum[i];
}
int ans = 0, mid = (l + r) >> 1;
if (L <= mid) {
ans += query(ls[i], l, mid, L, R);
}
if (R > mid) {
ans += query(rs[i], mid + 1, r, L, R);
}
return ans;
}
struct Sam {
int next[maxn << 1][26];
int link[maxn << 1], step[maxn << 1];
int endpos[maxn << 1];
int a[maxn], b[maxn << 1];
int sz, last, root;
void init() {
//如多次建立自动机,加入memset操作
root = sz = last = 1;
}
void add(int c) {
if (next[last][c] && step[last] + 1 == step[next[last][c]]) {
last = next[last][c];
return;
}
int p = last;
int np = ++sz;
last = np;
step[np] = step[p] + 1;
while (!next[p][c] && p) {
next[p][c] = np;
p = link[p];
}
if (p == 0) {
link[np] = root;
} else {
int q = next[p][c];
if (step[p] + 1 == step[q]) {
link[np] = q;
} else {
int nq = ++sz;
memcpy(next[nq], next[q], sizeof(next[q]));
step[nq] = step[p] + 1;
link[nq] = link[q];
link[q] = link[np] = nq;
while (next[p][c] == q && p) {
next[p][c] = nq;
p = link[p];
}
}
}
}
void build() {
init();
cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> s;
last = root;
for (int j = 0; j < s.length(); ++j) {
add(s[j] - 'a');
//need an update
update(endpos[last], 1, n, i);
}
}
for (int i = 1; i <= sz; i++) {
a[step[i]]++;
}
for (int i = 1; i <= sz; i++) {
a[i] += a[i - 1];
}
for (int i = 1; i <= sz; i++) {
b[a[step[i]]--] = i;
}
for (int i = sz; i > root; --i) {
int e = b[i];
endpos[link[e]] = merge(endpos[link[e]], endpos[e]);
}
}
int solve(int l, int r) {
int p = root;
for (int i = 0, c; i < t.length(); ++i) {
c = t[i] - 'a';
if (!next[p][c]) {
return 0;
}
p = next[p][c];
}
return query(endpos[p], 1, n, l, r);
}
} sam;
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
sam.build();
cin >> q;
int l, r, ans = 0;
while (q--) {
cin >> l >> r >> t;
l ^= ans, r ^= ans;
ans = sam.solve(l, r);
cout << ans << '\n';
}
return 0;
}

335

被折叠的 条评论
为什么被折叠?



