【思路要点】
- 考虑没有修改的情况,建立 TTT 的 ACACAC 自动机,用线段树维护 SSS 。
- 在线段树上维护从自动机上任意一点出发,经过区间中每一个字符后产生的匹配次数 cnkcnkcnk ,以及到达的节点 destdestdest ,合并该信息是 O(∑∣T∣)O(\sum|T|)O(∑∣T∣) 的。
- 有修改时,我们需要能够对被赋值的区间快速计算所维护的区间。
- 即我们需要快速计算形如 (i,from,len)(i,from,len)(i,from,len) 的询问,表示从第 iii 个循环串的第 fromfromfrom 个位置开始在自动机上走区间长度 lenlenlen 步,所维护的信息。
- 对加入的循环串的每个位置倍增计算 (i,from,2j)(i,from,2^j)(i,from,2j) ,并将 ∣S∣|S|∣S∣ 补足 222 的次幂,这样就可以在下传标记时直接调用所需的信息。
- 时间复杂度 O(∑∣T∣(N+Q)LogN)O(\sum|T|(N+Q)LogN)O(∑∣T∣(N+Q)LogN) 。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 2e5 + 5; const int MAXM = 1e5 + 5; const int MAXLOG = 18; const int MAXS = 55; typedef long long ll; typedef long double ld; typedef unsigned long long ull; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } namespace ACAutomaton { const int MAXP = 55; const int MAXC = 26; int root, size, key[MAXP]; int child[MAXP][MAXC], cnt[MAXP]; int depth[MAXP], fail[MAXP], last[MAXP]; void init() {root = size = 0; } int index(char x) { return x - 'a'; } void insert(char *s) { int len = strlen(s + 1), now = root; for (int i = 1; i <= len; i++) { int tmp = index(s[i]); if (child[now][tmp] == 0) { child[now][tmp] = ++size; depth[size] = depth[now] + 1; } now = child[now][tmp]; } key[now]++; } void build() { static int q[MAXP]; int l = 0, r = -1; for (int i = 0; i < MAXC; i++) if (child[root][i]) { q[++r] = child[root][i]; fail[child[root][i]] = root; last[child[root][i]] = root; } while (l <= r) { int tmp = q[l++]; for (int i = 0; i < MAXC; i++) if (child[tmp][i]) { q[++r] = child[tmp][i]; fail[child[tmp][i]] = child[fail[tmp]][i]; } else child[tmp][i] = child[fail[tmp]][i]; cnt[tmp] = cnt[fail[tmp]] + key[tmp]; last[tmp] = key[fail[tmp]] ? fail[tmp] : last[fail[tmp]]; } } } struct info { int dest[MAXS]; int cntk[MAXS]; }; info operator + (info a, info b) { info ans; memset(ans.dest, 0, sizeof(ans.dest)); memset(ans.cntk, 0, sizeof(ans.cntk)); for (int i = 0; i <= ACAutomaton :: size; i++) { ans.dest[i] = b.dest[a.dest[i]]; ans.cntk[i] = b.cntk[a.dest[i]] + a.cntk[i]; } return ans; } info unit(char c) { info ans; memset(ans.dest, 0, sizeof(ans.dest)); memset(ans.cntk, 0, sizeof(ans.cntk)); for (int i = 0; i <= ACAutomaton :: size; i++) { ans.dest[i] = ACAutomaton :: child[i][c - 'a']; ans.cntk[i] = ACAutomaton :: cnt[ACAutomaton :: child[i][c - 'a']]; } return ans; } char s[MAXN]; info trans[MAXM][MAXLOG]; int dest[MAXM][MAXLOG]; int n, m, T, Log, N, tot, ql[MAXN], qr[MAXN], belong[MAXN]; struct SegmentTree { struct Node { int lc, rc, Log, tag; info ans; } a[MAXN * 2]; int n, root, size; void update(int root) { a[root].ans = a[a[root].lc].ans + a[a[root].rc].ans; } void build(int &root, int l, int r, int Log) { a[root = ++size].Log = Log; if (l == r) { a[root].ans = unit(s[l]); return; } int mid = (l + r) / 2; build(a[root].lc, l, mid, Log - 1); build(a[root].rc, mid + 1, r, Log - 1); update(root); } void init(int x, int y) { n = x, root = size = 0; build(root, 1, n, y); } void pushdown(int root) { if (a[root].tag) { a[a[root].lc].tag = a[root].tag; a[a[root].lc].ans = trans[a[a[root].lc].tag][a[a[root].lc].Log]; a[a[root].rc].tag = dest[a[a[root].lc].tag][a[a[root].lc].Log]; a[a[root].rc].ans = trans[a[a[root].rc].tag][a[a[root].rc].Log]; a[root].tag = 0; } } void modify(int root, int l, int r, int ql, int qr, int &s) { if (l == ql && r == qr) { a[root].tag = s; a[root].ans = trans[s][a[root].Log]; s = dest[s][a[root].Log]; return; } pushdown(root); int mid = (l + r) / 2; if (ql <= mid) modify(a[root].lc, l, mid, ql, min(qr, mid), s); if (mid + 1 <= qr) modify(a[root].rc, mid + 1, r, max(mid + 1, ql), qr, s); update(root); } void modify(int l, int r, int s) { modify(root, 1, n, l, r, s); } info query(int root, int l, int r, int ql, int qr) { if (l == ql && r == qr) return a[root].ans; pushdown(root); int mid = (l + r) / 2; if (mid >= qr) return query(a[root].lc, l, mid, ql, qr); if (mid + 1 <= ql) return query(a[root].rc, mid + 1, r, ql, qr); return query(a[root].lc, l, mid, ql, mid) + query(a[root].rc, mid + 1, r, mid + 1, qr); } int query(int l, int r) { return query(root, 1, n, l, r).cntk[0]; } } ST; int main() { freopen("b.in", "r", stdin); freopen("b.out", "w", stdout); read(T), read(m); while (T--) { char s[MAXS]; scanf("\n%s", s + 1); ACAutomaton :: insert(s); } ACAutomaton :: build(); scanf("\n%s", s + 1); n = strlen(s + 1), N = 1, Log = 0; while (N < n) N <<= 1, Log++; while (n < N) s[++n] = 'a'; ST.init(n, Log), n = 0; for (int i = 1; i <= m; i++) { int opt, l, r; read(opt), read(l), read(r); if (opt == 1) { scanf("\n%s", s + n + 1); ql[++tot] = n + 1, qr[tot] = ql[tot] + strlen(s + n + 1) - 1, n = qr[tot]; for (int i = ql[tot]; i <= qr[tot]; i++) { trans[i][0] = unit(s[i]); dest[i][0] = (i == qr[tot]) ? ql[tot] : (i + 1); } for (int p = 1; p < MAXLOG; p++) for (int i = ql[tot]; i <= qr[tot]; i++) { trans[i][p] = trans[i][p - 1] + trans[dest[i][p - 1]][p - 1]; dest[i][p] = dest[dest[i][p - 1]][p - 1]; } ST.modify(l, r, ql[tot]); } else writeln(ST.query(l, r)); } return 0; }