题意:以trie的形式给出n个字符串,每次询问第x个字符串在第y个字符串中出现了几次。

解:总串长是n2级别的,所以不能用什么后缀自动机...

[update]可以建triesam但是不知道trie上的一个节点对应到了sam上的哪几个节点...感觉可以做...好混乱

听说是个AC自动机上DP,然后怎么想状态都是n²的,然后看题解发现跟DP没啥关系...

是这样的:对于x在y中出现的每一次,我们把它的前面一直补齐,这样每个串就对应着y的一个前缀,且这个前缀的一个后缀是x。

可知,trie上y节点到根的链就代表这些前缀。

然后要求后缀是x。因为x也在这些串中出现了,所以这些前缀一直跳fail的话一定会跳到x节点。即他们全都在x的fail树的子树中。

然后我们就发现,trie上y到根的路径上的节点和fail树上x的子树的节点的交就是答案。

一个很朴素的想法就是把这两棵树剖出来,就是一个二维数点问题。把主席树建出来,因为用了树剖所以每次回答询问是nlog²n的,可以做到在线。

一个高端想法就是离线下来,按照fail树建DFS序。在trie上DFS,维护trie树上当前节点到根的路径权值为1,别的地方为0。

然后查询就是当在trie上DFS到y的时候,查询x在fail树中的子树权值和。

这是单点修改,区间查询。树状数组即可。

 #include <cstdio>
#include <cstring>
#include <algorithm>
#include <stack>
#include <queue>
#include <vector> const int N = ; struct Edge {
int nex, v;
}edge[N << ]; int top; struct Node {
int x, y, ans;
}node[N]; int tr[N][], fail[N], tot = ;
char str[N];
int ed[N], e[N], pos[N], siz[N], num;
std::vector<int> v[N]; namespace bit {
int ta[N];
inline void add(int p) {
for(int i = p; i < N; i += i & (-i)) {
ta[i]++;
}
return;
}
inline void del(int p) {
for(int i = p; i < N; i += i & (-i)) {
ta[i]--;
}
return;
}
inline int getSum(int p) {
int ans = ;
for(int i = p; i >= ; i -= i & (-i)) {
ans += ta[i];
}
return ans;
}
inline int ask(int l, int r) {
return getSum(r) - getSum(l - );
}
} inline void add(int x, int y) {
top++;
edge[top].v = y;
edge[top].nex = e[x];
e[x] = top;
return;
} inline void getAC() {
std::stack<int> S;
std::queue<int> Q;
int n = strlen(str), t = , p = ;
for(int i = ; i < n; i++) {
if(str[i] == 'P') {
ed[++t] = p;
continue;
}
if(str[i] == 'B') {
if(!S.empty()) {
p = S.top();
S.pop();
}
continue;
}
int f = str[i] - 'a';
if(!tr[p][f]) {
tr[p][f] = ++tot;
}
S.push(p);
p = tr[p][f];
}
// getfail
fail[] = ;
Q.push();
while(!Q.empty()) {
int x = Q.front();
Q.pop();
for(int f = ; f < ; f++) {
if(!tr[x][f]) {
continue;
}
int y = tr[x][f], j = fail[x];
while(!tr[j][f] && j != ) {
j = fail[j];
}
if(tr[j][f] && x != ) {
j = tr[j][f];
}
fail[y] = j;
add(j, y);
Q.push(y);
}
}
return;
} void DFS_1(int x) {
pos[x] = ++num;
siz[x] = ;
for(int i = e[x]; i; i = edge[i].nex) {
int y = edge[i].v;
DFS_1(y);
siz[x] += siz[y];
}
return;
} void DFS_2(int x) {
bit::add(pos[x]);
for(int i = ; i < v[x].size(); i++) {
int t = node[v[x][i]].x;
node[v[x][i]].ans = bit::ask(pos[ed[t]], pos[ed[t]] + siz[ed[t]] - );
}
for(int i = ; i < ; i++) {
if(tr[x][i]) {
DFS_2(tr[x][i]);
}
}
bit::del(pos[x]);
return;
} int main() {
scanf("%s", str);
//
getAC();
DFS_1();
int m;
scanf("%d", &m);
for(int i = ; i <= m; i++) {
scanf("%d%d", &node[i].x, &node[i].y);
v[ed[node[i].y]].push_back(i);
} DFS_2(); for(int i = ; i <= m; i++) {
printf("%d \n", node[i].ans);
}
return ;
}

AC代码

洛谷P2414 阿狸的打字机的更多相关文章

  1. 洛谷P2414 阿狸的打字机【AC自动机】【fail树】【dfs序】【树状数组】

    居然真的遇上了这种蔡队题.瑟瑟发抖. 题目背景 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机. 题目描述 打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母.经阿 ...

  2. 洛谷P2414 阿狸的打字机 [NOI2011] AC自动机+树状数组/线段树

    正解:AC自动机+树状数组/线段树 解题报告: 传送门! 这道题,首先想到暴力思路还是不难的,首先看到y有那么多个,菜鸡如我还不怎么会可持久化之类的,那就直接排个序什么的然后按顺序做就好,这样听说有7 ...

  3. 洛谷 P2414 [NOI2011]阿狸的打字机 解题报告

    P2414 [NOI2011]阿狸的打字机 题目背景 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机. 题目描述 打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母 ...

  4. 洛谷P2414 - [NOI2011]阿狸的打字机

    Portal Description 首先给出一个只包含小写字母和'B'.'P'的操作序列\(s_0(|s_0|\leq10^5)\).初始时我们有一个空串\(t\),依次按\(s_0\)的每一位进行 ...

  5. 【AC自动机】【树状数组】【dfs序】洛谷 P2414 [NOI2011]阿狸的打字机 题解

        这一题是对AC自动机的充分理解和树dfs序的巧妙运用. 题目背景 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机. 题目描述 打字机上只有28个按键,分别印有26个小写英文字母和' ...

  6. 洛谷P2414 [NOI2011]阿狸的打字机(AC自动机)

    传送门 考虑一下,如果串B在串A中出现过,那么A的fail指针必定直接或间接指向B 那么我们可以把fail树建起来,那么就变成B代表的节点的子树里有多少节点属于A 然后这就是一个序列统计问题,直接用d ...

  7. 洛谷 P1383 codevs 3333 高级打字机

    题目描述 早苗入手了最新的高级打字机.最新款自然有着与以往不同的功能,那就是它具备撤销功能,厉害吧. 请为这种高级打字机设计一个程序,支持如下3种操作: 1.T x:在文章末尾打下一个小写字母x.(t ...

  8. P2414 [NOI2011]阿狸的打字机

    P2414 [NOI2011]阿狸的打字机 AC自动机+树状数组 优质题解 <------题目分析 先AC自动机搞出Trie图 然后根据fail指针建一只新树 把树映射(拍扁)到一个序列上,用树 ...

  9. BZOJ2434: [NOI2011]阿狸的打字机(AC自动机+dfs序+树状数组)

    [NOI2011]阿狸的打字机 题目链接:https://www.luogu.org/problemnew/show/P2414 题目背景 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机. ...

随机推荐

  1. Laravel 的十八个最佳实践

    本文翻译改编自 Laravel 的十八个最佳实践 这篇文章并不是什么由 Laravel 改编的 SOLID 原则.模式等. 只是为了让你注意你在现实生活的 Laravel 项目中最常忽略的内容.   ...

  2. hive条件函数

    case相当于if,when相当于=:then是条件满足的结论.否则实行else后语句,一end结束

  3. vue 使用技巧总结 19.01

    组件中箭头函数的使用 不要使用箭头来定义任意生命周期钩子函数: 不要使用箭头来定义一个 methods 函数: 不要使用箭头来定义 computed 里的函数: 不要使用箭头定义 watch 里的函数 ...

  4. vue axios 封装(二)

    封装二: http.js import axios from 'axios' import storeHelper from './localstorageHelper' // 全局设置 const ...

  5. cuda编程-卷积优化

    CUDA Convolution https://www.evl.uic.edu/sjames/cs525/final.html Improve Image Processing Using GPU ...

  6. ffmpeg 转码命令与ffplay

    ffmpeg.exe用于视频的转码. ▫ 最简单的命令 ffmpeg -i input.avi -b:v 640k output.ts 该命令将当前文件夹下的input.avi文件转换为output. ...

  7. 4、new一个对象的时候,初始化顺序:

    父类静态块>子类静态块> 父类属性(先系统默认值,后直接你赋予的值) >父类构造器>子类属性>子类构造器

  8. Cetos 7 防火墙设置

    1.关闭防火墙: # systemctl stop firewalld.service 2.开启防火墙: # systemctl start firewalld.service 3.关闭开机启动: # ...

  9. G - Balanced Lineup POJ - 3264 线段树最大最小值区间查询模版题

    题意 给出一个序列  每次查询区间的max-min是多少 思路:直接维护max 和min即可  写两个query分别查最大最小值 #include<cstdio> #include< ...

  10. SpringBoot添加CORS跨域

    配置CORSConfiguration 添加CORS的配置信息,我们创建一个CORSConfiguration配置类重写如下方法,如图所示: @Override public void addCors ...