题解:CF163E e-Government
Statement 解析
给了字符串集合 \(\{S\}\),有 \(N\) 次操作,另有与字符串集合 \(\{S\}\) 相同的 \(\{T\}\),每次给出一种操作:
- 选择编号为 \(i\) 的字符串加入 \(\{T\}\),已存在于 \(\{T\}\) 中不执行。
- 从 \(\{T\}\) 中删除编号为 \(i\) 的字符串吗,不存在于 \(\{T\}\) 中不执行。
- 询问,给定一个字符串 \(S'\),求当前集合 \(\{T\}\) 中每个串在 \(S'\) 中的出现次数之和。
Solution
看来是比较典的 AC 自动机的题目。
首先我们摸出一个 trick:求子串可以枚举前缀然后考虑这段前缀的后缀。
那么我们对集合 \(\{T\}\) 建 AC 自动机,那么当我们从根往下走的时候就是在枚举前缀(因为是 trie 树),然后处理 \(fail\) 指针,根据 \(fail\) 的含义:指向当前前缀的最长后缀的尾,我们可以通过跳 \(fail\) 得到这段前缀的后缀,每个点都有且仅有一个 \(fail\) 指针,那么从 \(fail_u \to u\) 建边得到的就是一棵外向树(有向且从根指向叶子),原本我们想要的是从 \(u\) 跳 \(fail_u\) 假设最后跳到 \(v\),那么在新建出来的树上 \(v\) 在 \(u\) 子树内且从 \(fail_u\) 到 \(u\) 子树内任意结点都是一种可行匹配,那么这就是个子树和问题。
但是这道题带删带修,其实再摸出一个 trick:子树内 \(dfn\) 连续,所以可以在 \(dfn\) 子树加一或者减一然后上传区间和,暴力单点查统计,线段树或者树状数组维护都可。
Code
#include <bits/stdc++.h>
//#define int long long
#define son(rt,x) Amt[rt].son[x]
#define fail(rt) Amt[rt].fail
#define lson(rt) (rt << 1)
#define rson(rt) (rt << 1 | 1)
#define len(rt) (Sgt[rt].r - Sgt[rt].l + 1)
using namespace std;
const int MAXN = 1e6 + 10;
int N, K, cnt, ind, idx;
int tail[MAXN], low[MAXN], dfn[MAXN], head[MAXN];
bool tag[MAXN];
string Style, Text;
struct Graphon {
int to, nxt;
} E[MAXN];
struct AutoMaton {
int son[26];
int fail;
} Amt[MAXN];
struct Sgton {
int l, r, sum, lazy;
} Sgt[MAXN << 2];
inline void Addedge (int u, int v) { E[idx].to = v, E[idx].nxt = head[u], head[u] = idx++; }
inline void InsertString (int ID) {
int len = Style.length(), now = 0;
for (int i = 0; i < len; i ++) {
int chVal = Style[i] - 'a';
if (!son(now, chVal))
son(now, chVal) = ++cnt;
now = son(now, chVal);
}
tail[ID] = now;
}
inline void GetFailPointer() {
queue<int> q;
for (int i = 0; i < 26; i ++) {
if (son(0, i)) {
q.emplace(son(0, i));
Addedge (0, son(0, i));
}
}
while (!q.empty()) {
int now = q.front(); q.pop();
for (int i = 0; i < 26; i ++) {
if (!son(now, i))
son(now, i) = son(fail(now), i);
else {
fail(son(now, i)) = son(fail(now), i);
q.emplace (son(now, i));
Addedge (fail(son(now, i)), son(now, i));
}
}
}
}
void dfs (int from) {
dfn[from] = ++ind;
for (int i = head[from]; ~i; i = E[i].nxt) dfs (E[i].to);
low[from] = ind;
}
void Build (int l, int r, int rt) {
Sgt[rt].l = l, Sgt[rt].r = r;
if (l == r) return;
int mid = (l + r) >> 1;
Build (l, mid, lson(rt));
Build (mid + 1, r, rson(rt));
}
inline void pushdown (int rt) {
if (Sgt[rt].lazy) {
Sgt[lson(rt)].lazy += Sgt[rt].lazy;
Sgt[rson(rt)].lazy += Sgt[rt].lazy;
Sgt[lson(rt)].sum += len(lson(rt)) * Sgt[rt].lazy;
Sgt[rson(rt)].sum += len(rson(rt)) * Sgt[rt].lazy;
Sgt[rt].lazy = 0;
}
}
void Modify (int ql, int qr, int rt, int val) {
int l = Sgt[rt].l, r = Sgt[rt].r;
if (ql <= l && qr >= r) {
Sgt[rt].lazy += val;
Sgt[rt].sum += len(rt) * val;
return;
}
int mid = (l + r) >> 1;
pushdown(rt);
if (ql <= mid)
Modify (ql, qr, lson(rt), val);
if (qr > mid)
Modify (ql, qr, rson(rt), val);
Sgt[rt].sum = Sgt[lson(rt)].sum + Sgt[rson(rt)].sum;
}
int query (int ql, int qr, int rt) {
int l = Sgt[rt].l, r = Sgt[rt].r;
if (ql <= l && qr >= r)
return Sgt[rt].sum;
int mid = (l + r) >> 1, res = 0;
pushdown(rt);
if (ql <= mid)
res += query (ql, qr, lson(rt));
if (qr > mid)
res += query (ql, qr, rson(rt));
return res;
}
inline int queryAns (string str) {
int len = str.length(), now = 0, res = 0;
for (int i = 1; i < len; i ++) {
int chVal = str[i] - 'a';
now = son(now, chVal);
res += query (dfn[now], dfn[now], 1);
}
return res;
}
inline int chgStr (string str) {
int len = str.length(), res = 0;
for (int i = 1; i < len; i ++)
res = res * 10 + str[i] - '0';
return res;
}
signed main() {
ios_base::sync_with_stdio (false);
cin.tie (nullptr), cout.tie (nullptr);
cin >> N >> K, cnt = ind = idx = 0;
memset (head, -1, sizeof head);
for (int i = 1; i <= K; i ++)
cin >> Style, InsertString(i);
GetFailPointer(), dfs(0), Build (1, ind, 1);
for (int i = 1; i <= K; i ++)
tag[i] = true, Modify (dfn[tail[i]], low[tail[i]], 1, 1);
for (int i = 1; i <= N; i ++) {
cin >> Text;
if (Text[0] == '+') {
int strVal = chgStr(Text);
// cout << strVal << "\n";
if (!tag[strVal])
tag[strVal] = true, Modify (dfn[tail[strVal]], low[tail[strVal]], 1, 1);
} else if (Text[0] == '-') {
int strVal = chgStr(Text);
// cout << strVal << "\n";
if (tag[strVal])
tag[strVal] = false, Modify (dfn[tail[strVal]], low[tail[strVal]], 1, -1);
} else {
cout << queryAns(Text) << "\n";
}
}
return 0;
}

浙公网安备 33010602011771号