洛谷 P2414 / LOJ 2444 「NOI2011」阿狸的打字机
思路
首先套路地建 AC 自动机,并存下每一行的终止结点,记为 。
考虑在 AC 自动机上匹配的过程, 在 中出现的次数就相当于在 Trie 树上 到根结点的链上,每个结点都不断跳 fail,有多少个结点是 ,也就是在 fail 树上有多少个结点在 的子树内。
因此我们先将询问离线,将每一组询问 挂到 上,然后建出 fail 树并求出每个结点的 (即子树 序的范围)。在原 Trie 上 dfs 并处理所有 的询问。每次递归开始时将 设为 ,回溯时再减回去。这样当遍历到结点 时,只有 到根结点的链上的点为 。于是可以处理每个挂在 上的询问,树状数组求出。
代码
code
/* p_b_p_b txdy AThousandMoon txdy AThousandSuns txdy hxy txdy */ #include <bits/stdc++.h> #define pb push_back #define fst first #define scd second using namespace std; typedef long long ll; typedef pair<int, int> pii; const int maxn = 100100; int n, m, a[maxn], ans[maxn]; int c[maxn], st[maxn], ed[maxn], times; int head[maxn], len; char s[maxn]; vector<pii> qq[maxn]; struct edge { int to, next; } edges[maxn << 1]; void add_edge(int u, int v) { edges[++len].to = v; edges[len].next = head[u]; head[u] = len; } inline int lowbit(int x) { return x & (-x); } inline void update(int x, int d) { for (int i = x; i <= times; i += lowbit(i)) { c[i] += d; } } inline int query(int x) { int res = 0; for (int i = x; i; i -= lowbit(i)) { res += c[i]; } return res; } struct AC { int ch[maxn][26], och[maxn][26], fa[maxn]; int fail[maxn], tot; void insert(char *s) { int p = 0; for (int i = 0; s[i]; ++i) { if (islower(s[i])) { if (!ch[p][s[i] - 'a']) { ch[p][s[i] - 'a'] = ++tot; fa[tot] = p; } p = ch[p][s[i] - 'a']; } else if (s[i] == 'B') { p = fa[p]; } else { a[++n] = p; } } for (int i = 0; i <= tot; ++i) { for (int j = 0; j < 26; ++j) { och[i][j] = ch[i][j]; } } } void build() { queue<int> q; for (int i = 0; i < 26; ++i) { if (ch[0][i]) { q.push(ch[0][i]); } } while (q.size()) { int u = q.front(); q.pop(); for (int i = 0; i < 26; ++i) { if (ch[u][i]) { fail[ch[u][i]] = ch[fail[u]][i]; q.push(ch[u][i]); } else { ch[u][i] = ch[fail[u]][i]; } } } } void dfs(int u) { st[u] = ++times; for (int i = head[u]; i; i = edges[i].next) { int v = edges[i].to; dfs(v); } ed[u] = times; } void dfs2(int u) { update(st[u], 1); for (pii p : qq[u]) { ans[p.scd] = query(ed[p.fst]) - query(st[p.fst] - 1); } for (int i = 0; i < 26; ++i) { if (och[u][i]) { dfs2(och[u][i]); } } update(st[u], -1); } void solve() { for (int i = 1; i <= tot; ++i) { add_edge(fail[i], i); } dfs(0); dfs2(0); for (int i = 1; i <= m; ++i) { printf("%d\n", ans[i]); } } } ac; void solve() { scanf("%s%d", s, &m); ac.insert(s); ac.build(); for (int i = 1; i <= m; ++i) { int x, y; scanf("%d%d", &x, &y); qq[a[y]].pb(make_pair(a[x], i)); } ac.solve(); } int main() { int T = 1; // scanf("%d", &T); while (T--) { solve(); } return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!