要点

  • 这是一道蔡队题,看我标题行事
  • 任意询问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. android程序的真正入口

    代码出自MtAndroid 3.1.2完全开发手册,适用于Android平台. 概述 android程序的真正入口是Application类的onCreate方法.它的继承关系如下所示: java.l ...

  2. 分享知识-快乐自己:什么是MVC

    1.什么是mvc: Model View Controller,是模型-视图-控制器的缩写,一种软件设计典范,用一种业务逻辑.数据.界面显示分离的方法组织代码,将业务逻辑聚集到一个组件里,在改进和个性 ...

  3. 分享知识-快乐自己:java 中的访问修饰符

    1):Java中的访问修饰符: Java面向对象的基本思想之一是封装细节并且公开接口.Java语言采用访问控制修饰符来控制类及类的方法和变量的访问权限,从而向使用者暴露接口,但隐藏实现细节. 访问控制 ...

  4. Mac使用记录

    ---恢复内容开始--- brew list //查看brew安装东东 ls //当前目录下内容 brew --cache //查看brew下载目录 /usr/local/Cellar/ //隐藏文件 ...

  5. PHP留言小练习

    实现功能: 留言.搜索.编辑.删除.详情页.时间.点击量 页面划分: index.html(留言列表页) add.html(留言页) edit.php(编辑页) del.php(删除页) view.p ...

  6. Git Shell Warning

    Warning: Permanently added 'github.com,192.30.252.120' <RSA> to the list of known hosts. The a ...

  7. 3170: [Tjoi 2013]松鼠聚会

    题目大意 给定n个点,找到一个点使这个点到其他所有点的切比雪夫距离之和最小. 题解 我们知道切比雪夫距离和曼哈顿距离的转化公式 \(1\)表示切比雪夫距离,\(2\)表示曼哈顿距离 我们有: \(x_ ...

  8. 【Lintcode】062.Search in Rotated Sorted Array

    题目: Suppose a sorted array is rotated at some pivot unknown to you beforehand. (i.e., 0 1 2 4 5 6 7  ...

  9. 用OpenLayers开发地图应用

    项目背景 最近有一个使用全球地图展示数据的项目,用地图展示数据本身没什么难度,但出于安全和保密的考虑,甲方单位要求项目不能连接外网,只能在内网使用,也就是说,我们不得不在内网中部署一个地图服务器,在这 ...

  10. saltstack其他运行模式

    除了常规的运行模式外,salt还有几种运行模式 salt-call --local可以直接在minion上自执行,多用于本机自测试,此方式几乎不用,知道即可 [root@linux-node2 ~]# ...