要点

  • 这是一道蔡队题,看我标题行事
  • 任意询问y串上有多少个x串,暴力找每个节点是不是结尾肯定是炸的,考虑本质:如果某节点是x的结尾,根据ac自动机的性质,x一定是此(子)串后缀。又有每个Trie节点的fail只指向另一个节点,故有fail树的概念。问题就变成了“对于串x的尾节点,在fail树中它的子树中有多少个点是在y串上”
  • 解决方法是巧妙的。
  • 离线记录查询的信息。然后搜索原Trie树,遇到尾节点就扫描它有哪些查询,这里尾节点是y的尾节点。而当前搜索时如果我们在搜该点,则该点计数++,搜完它的子树回溯了,该点计数--,这样做使得搜到尾节点时,只有这个字符串上的节点才有计数,达到了想要的效果:只有串y的节点才有计数。
  • 那么现在y上的所有节点都被计数了,怎样统计有多少个是在x的fail子树上呢?就是在之前预处理dfs序,子树的常规操作。这样计数是在dfn上进行的,维护和查询用一下树状数组即可,想查询x的子树有多少值就直接查询前缀和即可。
  • 注意除了思路以外还有写法上的优化,就题论板子,比如这题常规地insert就会T,发现题目特殊性质可以特殊插入,大大加快了速度。
  • 总的来讲虽然标题很花哨但是操作都是中规中矩的,需要什么映射的数组就开一下就是了。其实没处理鲁棒性使得一些数据能hack掉我的代码,比如空串还删或者出现相同的串,但没想到A了那就懒得改了。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <string>
#include <queue>
#include <vector>
#include <map>
using namespace std; const int N = 1e5 + 5; char op[N], t[N];
int m, cnt;
vector<pair<int, int>> query[N];
int trie[N][26], dfn[N], size[N], Time;
int x, y, ans[N]; int ch[N][26];//Trie树的转移
int fa[N];
int val[N];//根据题意赋值。有值则意味着某子串末尾
int fail[N];//失配,转移到别的树枝接着找
int sz;//注意这个板子sz一定是要从1开计
int book[N];
vector<int> ftr[N]; void getfail() {
queue<int> Q;
for (int i = 0; i < 26; i++)
if (ch[0][i]) {
fail[ch[0][i]] = 0;
ftr[0].push_back(ch[0][i]);
Q.push(ch[0][i]);//第二层指向根
}
while (!Q.empty()){
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];
if (val[ch[u][i]] != -1)
ftr[ch[fail[u]][i]].push_back(ch[u][i]);
Q.push(ch[u][i]);//指向其他枝上同样的字母
} else ch[u][i] = ch[fail[u]][i];//使得find时半路突然失配时还能一下拐回去
}
} void dfs(int now) {
dfn[now] = ++Time;
size[now] = 1;
for (int i : ftr[now]) {
dfs(i);
size[now] += size[i];
}
} struct BIT {
int F[N]; void add(int x, int val) {
for (; x <= Time; x += x&-x)
F[x] += val;
} int ask(int x) {
int res = 0;
for (; x; x -= x&-x)
res += F[x];
return res;
}
}bit; void Dfs(int cur) {
int y = val[cur];
bit.add(dfn[cur], 1);
if (y != -1) {
for (auto i : query[y]) {
int sz = book[i.first];
ans[i.second] = bit.ask(dfn[sz] + size[sz] - 1) - bit.ask(dfn[sz] - 1);
}
}
for (int i = 0; i < 26; i++) {
if (trie[cur][i])
Dfs(trie[cur][i]);
}
bit.add(dfn[cur], -1);
} int main() {
memset(val, -1, sizeof val);
scanf("%s", op);
int L = strlen(op), now = 0;
for (int i = 0; i < L; i++) {
if (op[i] == 'B') {
now = fa[now];
} else if (op[i] == 'P') {
val[now] = ++cnt;
book[cnt] = now;
} else {
if (!ch[now][op[i] - 'a']) {
trie[now][op[i] - 'a'] = ch[now][op[i] - 'a'] = ++sz;
val[sz] = 0;
fa[sz] = now;
}
now = ch[now][op[i] - 'a'];
}
} getfail();
dfs(0);
scanf("%d", &m);
for (int i = 1; i <= m; i++) {
scanf("%d %d", &x, &y);
query[y].push_back({x, i});
}
Dfs(0);
for (int i = 1; i <= m; i++) {
printf("%d\n", ans[i]);
}
}

洛谷2414(构建ac自动机fail树dfs序后遍历Trie树维护bit及询问答案)的更多相关文章

  1. AC自动机fail树上dfs序建线段树+动态memset清空

    题意:http://acm.hdu.edu.cn/showproblem.php?pid=4117 思路:https://blog.csdn.net/u013306830/article/detail ...

  2. 洛谷P3808 & P3796 AC自动机模板

    题目:P3808:https://www.luogu.org/problemnew/show/P3808 P3796:https://www.luogu.org/problemnew/show/P37 ...

  3. 洛谷 - P3966 - 单词 - AC自动机

    https://www.luogu.org/problemnew/show/P3966 因为文本串就是字典本身,所以这个和平时的AC自动机不太一样.平时的query要沿着fail树把子树的出现次数依次 ...

  4. CodeForces - 1207G :Indie Album(AC自动机 fail树上DFS)

    题意:有N个串,给出的形式是拼接给出,对于第i行:  (1,c)表示字符串i是单个字母c: (2,p,c)表示字符串i=在字符串p后面接上一个字母c. 然后给出M个提问,形式是(i,string).问 ...

  5. 洛谷.3121.审查(AC自动机 链表)

    题目链接 //删掉一个单词需要前移一段位置,用链表维护就好了 复杂度O(sum(len)) #include <cstdio> #include <cstring> #defi ...

  6. 洛谷 - P2444 - 病毒 - AC自动机

    https://www.luogu.org/problemnew/show/P2444 有点恶心,不太明白fail的意义. #include<bits/stdc++.h> using na ...

  7. 洛谷3320 SDOI2015寻宝游戏(set+dfs序)(反向迭代器的注意事项!)

    被\(STL\)坑害了一个晚上,真的菜的没救了啊. 准确的说是一个叫\(reverse\ iterator\)的东西,就是我们经常用的\(rbegin()\) 有一个非常重要的性质 在反向迭代器中,+ ...

  8. BZOJ 3551/3545: [ONTAK2010]Peaks加强版 (Kruskal树+dfs序上的主席树+倍增)

    Orz PoPoQQQ 学到了维护子树信息的时候用dfsdfsdfs序套主席树节省线段树空间. 学到了怎么用指针写可持久化线段树-emmm- CODE 只贴上3551加强版带强制在线的代码 #incl ...

  9. BZOJ2434 [Noi2011]阿狸的打字机 【AC自动机 + fail树 + 树状数组】

    2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec  Memory Limit: 256 MB Submit: 3610  Solved: 1960 [Submit][S ...

随机推荐

  1. 2048聚合版开源代码,cocos2d-js编写,基于CocosEditor开发工具,可运行Android,ios,html5等

    1. [代码][JavaScript]代码         /** * @GameName : * 2048 * * @DevelopTool: * Cocos2d-x Editor (CocosEd ...

  2. tensorflow 实现逻辑回归——原以为TensorFlow不擅长做线性回归或者逻辑回归,原来是这么简单哇!

    实现的是预测 低 出生 体重 的 概率.尼克·麦克卢尔(Nick McClure). TensorFlow机器学习实战指南 (智能系统与技术丛书) (Kindle 位置 1060-1061). Kin ...

  3. 【Lintcode】382.Triangle Count

    题目: Given an array of integers, how many three numbers can be found in the array, so that we can bui ...

  4. 关于Django ORM filter方法小结

    django filter是一个过滤器,相当于SQL的select * from where. filter返回一个QuerySet对象,还可以在该对象上继续进行django orm 该有的操作. 有 ...

  5. tyvj1124花店橱窗——有限制的DP

    题目:http://www.joyoi.cn/problem/tyvj-1124 此题首先注意一束花能往哪些花瓶中放置:原本应放在本位置,但因为有多余花瓶,故可以有些变动,范围在i~i+v-f间: 假 ...

  6. printf函数指向串口的方法

    简单地说:想在mdk 中用printf,需要同时重定义fputc函数和避免使用semihosting(半主机模式),标准库函数的默认输出设备是显示器,要实现在串口或LCD输出,必须重定义标准库函数里调 ...

  7. strlen("汉字")的值是多少

    转自:http://blog.csdn.net/gogor/article/details/4470775 strlen("汉字")的值是多少? 这个问题的答案与系统所采用的字符编 ...

  8. bzoj4176

    莫比乌斯反演 根据约数和个数公式 $ans = \sum_{i=1}^{n}\sum_{j=1}^{n}\sum_{x|i}\sum_{y|j}{[gcd(i, j)==1]}$ 交换枚举顺序 $an ...

  9. Hibernate错误:javax/persistence/EntityListeners

    1. 原文地址:http://heavengate.blog.163.com/blog/static/20238105320127291018026/ 错误信息: hibernate:javax/pe ...

  10. Hibernate&nbsp;Session的delete()方法

    本文介绍Hibernate Session的delete()方法.delete()方法用于从数据库中删除与Java对象对应的记录.对应游离对象和持久化对象,delete语句会做出不同的反应. AD:  ...