洛谷 P2414 / LOJ 2444 「NOI2011」阿狸的打字机

洛谷传送门

LOJ 传送门

思路

首先套路地建 AC 自动机,并存下每一行的终止结点,记为 ax

考虑在 AC 自动机上匹配的过程,xy 中出现的次数就相当于在 Trie 树上 ay 到根结点的链上,每个结点都不断跳 fail,有多少个结点是 ax,也就是在 fail 树上有多少个结点在 ax 的子树内。

因此我们先将询问离线,将每一组询问 (x,y) 挂到 ay 上,然后建出 fail 树并求出每个结点的 [stx,edx](即子树 dfn 序的范围)。在原 Trie 上 dfs 并处理所有 ay 的询问。每次递归开始时将 stu 设为 1,回溯时再减回去。这样当遍历到结点 k 时,只有 k 到根结点的链上的点为 1。于是可以处理每个挂在 k 上的询问,树状数组求出。

代码

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;
}
posted @   zltzlt  阅读(41)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示