要点

  • 这是一道蔡队题,看我标题行事
  • 任意询问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. 检测UTF-8编码

    在PHP检测字符串是否是UTF-8编码的时候,很多人在使用mb_detect_encoding的时候,经常遇到检测不准的问题,下面的方法可以准确检测编码是否是UTF-8 function check_ ...

  2. 思科安全:加密流量威胁检测、加密流量威胁和恶意软件检测、识别无线干扰或威胁、Talos 情报源可加强对已知和新型威胁的防御、分布式安全异常检测

    思科DNA竞品比较工具 您的网络能够驱动数字化转型吗? 根据IDC调查,45%的受调研公司计划在未来两年内做好网络数字化的准备.查看数字化网络带来的结果和商业价值. 下载报告 思科 HPE 华为 Ar ...

  3. 勤于思考:Objective-C特性的扩展

    赋值 assign:直接赋值.默认 @interface Car : NSObject { NSString *_name; } @property (assign,nonatomic) NSStri ...

  4. Python: scikit-image Blob detection

    这个用例主要介绍利用三种算法对含有blob的图像进行检测,blob 或者叫斑点,就是在一幅图像上,暗背景上的亮区域,或者亮背景上的暗区域,都可以称为blob.主要利用blob与背景之间的对比度来进行检 ...

  5. Python3解leetcode Best Time to Buy and Sell Stock II

    问题描述: Say you have an array for which the ith element is the price of a given stock on day i. Design ...

  6. Ubuntu vim使用

    vim安装:apt-get install vim-gtk vim使用:默认启动插入模式,或者按‘I’进入插入模式,退出插入模式按‘esc’,用冒号‘:wq’进行保存退出: 其复制和粘贴是靠鼠标右键中 ...

  7. 安装pillow

    最近想学Python的图像操作.首要任务就是安装pillow.这个强大的图形处理工具. 但是我遇到了一个问题. Collecting pilow  Could not find a version t ...

  8. 【opencv学习笔记一】opencv下载安装与VS2017开发环境配置

    本文章摘录自浅墨博客,原文链接http://blog.csdn.net/poem_qianmo/article/details/19809337 目录 1.opencv下载与安装 2.计算机环境变量配 ...

  9. JavaScript与DOM常见面试题

    1. JavaScript 1.1.简要描述 JavaScript的数据类型? 参考答案: Java Sc ri pt 的数据类型可以分为原始类型和对象类型.原始类型包括 string. number ...

  10. python斐波拉契数列

    def fib(max): n, a, b = 0, 0, 1 while n < max: print(b) a, b = b, a + b n = n + 1 return 'done' 注 ...