bzoj2434 阿狸的打字机
思路
可以发现,其实题目中所描述的操作,就是在\(AC\)自动机上走的过程。输出就是打上标记。删除就是返回父亲节点。
然后看询问。每次询问字符串\(x\)在字符串中\(y\)出现的次数。其实也就是问在\(AC\)自动机上的\(y\)这个字符串上,有多少位置的\(fail\)指针指向\(x\)的结尾。
所以想到将\(fail\)指针反过来,建立\(fail\)树。
先考虑只有一次询问的时候怎么做。只要将\(AC\)自动机上从根到\(y\)路径上的点在\(fail\)树上对应的变成\(1\)。然后询问以\(fail\)树上以\(x\)为根的子树有多少个\(1\)。
对于多次询问,可以将询问离线下来。然后在\(AC\)自动机上走。当新走到一个点的时候,就在\(fail\)树上这个点对应的\(++\)。当删除一个点的时候,就在\(fail\)树上这个点对应的\(--\)。当打印字符串的时候。就回答那些\(y\)为当前字符串的询问。
这样的单点修改,子树查询可以用\(dfs\)序+树状数组解决。
代码
/*
* @Author: wxyww
* @Date: 2019-02-01 10:28:18
* @Last Modified time: 2019-02-01 16:35:45
*/
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cmath>
#include<queue>
#include<ctime>
#include<cstring>
#include<algorithm>
#include<bitset>
using namespace std;
typedef long long ll;
const int N = 100000 + 100;
ll read() {
ll x=0,f=1;char c=getchar();
while(c<'0'||c>'9') {
if(c=='-') f=-1;
c=getchar();
}
while(c>='0'&&c<='9') {
x=x*10+c-'0';
c=getchar();
}
return x*f;
}
char s[N];
int dy[N];
int trie[N][26];
int ans[N];
struct node{
int x,y,id;
}que[N];
bool cmp(node x,node y) {
return x.y < y.y;
}
int tot = 1,n,now = 1;
int fa[N],bz[N],bzjs;
void solve() {
n = strlen(s + 1);
for(int i = 0;i <= 100010;++i) fa[i] = 1;
for(int i = 1;i <= n;++i) {
if(s[i] == 'P') {
bz[now] = ++bzjs;
dy[bzjs] = now;
continue;
}
if(s[i] == 'B') {
now = fa[now];
continue;
}
int x = s[i] - 'a';
if(!trie[now][x]) trie[now][x] = ++tot;
fa[trie[now][x]] = now;
now = trie[now][x];
}
}
queue<int>q;
vector<int>e[N];
int fail[N];
void get_fail() {
for(int i = 0;i <= tot;++i) fail[i] = 1;
for(int i = 0;i < 26;++i) if(trie[1][i]) q.push(trie[1][i]);
while(!q.empty()) {
int u = q.front();q.pop();
for(int i = 0;i < 26;++i) {
if(trie[u][i]) fail[trie[u][i]] = trie[fail[u]][i],q.push(trie[u][i]);
else trie[u][i] = trie[fail[u]][i];
}
}
for(int i = 2;i <= tot;++i) {
if(fail[i] == 0) fail[i] = 1;e[fail[i]].push_back(i);
}
}
int id[N],cnt,siz[N];
void dfs(int u) {
id[u] = ++cnt;
siz[u] = 1;
int k = e[u].size();
for(int i = 0;i < k;++i) {
int v = e[u][i];
dfs(v);
siz[u] += siz[v];
}
}
int pos = 1;
int tree[N];
void update(int pos,int c) {
while(pos <= tot) {
tree[pos] += c;
pos += pos & -pos;
}
}
int query(int pos) {
int anss = 0;
while(pos >= 1) {
anss += tree[pos];
pos -= pos & -pos;
}
return anss;
}
void work() {
now = 1;
for(int i = 1;i <= n;++i) {
if(s[i] == 'P') {
while(que[pos].y == bz[now]) {
ans[que[pos].id] = query(id[dy[que[pos].x]] + siz[dy[que[pos].x]] - 1) - query(id[dy[que[pos].x]] - 1);
++pos;
}
continue;
}
if(s[i] == 'B') {
update(id[now],-1);
now = fa[now];
continue;
}
int x = s[i] - 'a';
now = trie[now][x];
update(id[now],1);
}
}
int main() {
scanf("%s",s + 1);
int m = read();
for(int i = 1;i <= m;++i) {
que[i].id = i;
que[i].x = read(),que[i].y = read();
}
sort(que + 1,que + m + 1,cmp);
solve();
get_fail();
dfs(1);
work();
for(int i = 1;i <= m;++i) printf("%d\n",ans[i]);
return 0;
}
/*
aPaPBbP 3 1 2 1 3 2 3
*/
bzoj2434 阿狸的打字机的更多相关文章
- [NOI2011][bzoj2434] 阿狸的打字机 [AC自动机+dfs序+fail树+树状数组]
题面 传送门 正文 最暴力的 最暴力的方法:把所有询问代表的字符串跑一遍kmp然后输出 稍微优化一下:把所有询问保存起来,把模板串相同的合并,求出next然后匹配 但是这两种方法本质没有区别,都是暴力 ...
- 【BZOJ2434】阿狸的打字机(AC自动机,树状数组)
[BZOJ2434]阿狸的打字机(AC自动机,树状数组) 先写个暴力: 每次打印出字符串后,就插入到\(Trie\)树中 搞完后直接搭\(AC\)自动机 看一看匹配是怎么样的: 每次沿着\(AC\)自 ...
- 【BZOJ2434】【NOI2011】阿狸的打字机(AC自动机,树状数组)
[BZOJ2434]阿狸的打字机(AC自动机,树状数组) 先写个暴力: 每次打印出字符串后,就插入到\(Trie\)树中 搞完后直接搭\(AC\)自动机 看一看匹配是怎么样的: 每次沿着\(AC\)自 ...
- 【BZOJ2434】[NOI2011]阿狸的打字机 AC自动机+DFS序+树状数组
[BZOJ2434][NOI2011]阿狸的打字机 Description 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P ...
- 【bzoj2434】: [Noi2011]阿狸的打字机 字符串-AC自动机-BIT
[bzoj2434]: [Noi2011]阿狸的打字机 x串在y串上的匹配次数就是y在自动机所有节点上能够通过fail走到x最后一个节点的个数 (就是y串任意一个前缀的后缀能匹配到x的个数)和[bzo ...
- 【BZOJ-2434】阿狸的打字机 AC自动机 + Fail树 + DFS序 + 树状数组
2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 2022 Solved: 1158[Submit][Sta ...
- BZOJ2434 [Noi2011]阿狸的打字机 【AC自动机 + fail树 + 树状数组】
2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec Memory Limit: 256 MB Submit: 3610 Solved: 1960 [Submit][S ...
- BZOJ2434: [NOI2011]阿狸的打字机(AC自动机+dfs序+树状数组)
[NOI2011]阿狸的打字机 题目链接:https://www.luogu.org/problemnew/show/P2414 题目背景 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机. ...
- 洛谷P2414 - [NOI2011]阿狸的打字机
Portal Description 首先给出一个只包含小写字母和'B'.'P'的操作序列\(s_0(|s_0|\leq10^5)\).初始时我们有一个空串\(t\),依次按\(s_0\)的每一位进行 ...
随机推荐
- Granfana+PostgreSQL
建表: create table pdm_log( id serial primary key, date_time timestamp with time zone DEFAULT CURRENT_ ...
- Mysql优化单表查询
借助explain分析SQL,判断该怎么建立索引. 还需要注意,有些情况会导致索引失效,用不上索引,应该优化SQL,应用上索引. 什么情况导致索引失效? 1.在索引列上做任何操作(计算.函数.类型转换 ...
- python之路--BOM和DOM
一. 介绍 之前学的JS的一些简单的语法并没有和浏览器有任何的交互. 我们要想制作一些我们经常看到的网页的一些交互,我们需要继续学习BOM和DOM相关知识. JavaScript 分为 ECMAScr ...
- python之路--小数据池,再谈编码,is和 == 的区别
一 . 小数据池 # 小数据池针对的是: int, str, bool 在py文件中几乎所有的字符串都会缓存. # id() 查看变量的内存地址 s = 'attila' print(id(s)) 二 ...
- linux重装系统,如何保存硬盘中的内容
以前没有太关注重装系统如何保留下硬盘中的内容.但是最近有一些文件在重装系统后确实需要继续保留下来,于是花了点时间了解下磁盘分区相关的东东. 参考 http://blog.csdn.net/openn/ ...
- $.ajax的async设置true和false的区别一点笔记
async的默认值是true 当async为true时,为异步请求 如果一个$.ajax的函数在另一个函数中调用,不一定会等该函数调用完再加载完函数 导致产生空值的问题 而在JS函数中调用$.ajax ...
- query中对应的OnSetText和onGetText事件
今天在看代码的时候遇到一个问题,query中的某个字段和在表中显示的不是一个值,我觉得很奇怪,于是找了很久,才知道为什么,原来是query中的OnSetText和onGetText事件在作怪,经过一番 ...
- flask 保存文件到 七牛云
上篇文章队长讲述了如何把前端上传的文件保存到本地项目目录 本篇 讲述一下把前端上传的文件保存到 第三方存储(七牛云) 七牛云相关步骤思路: 首先 进去七牛云官网,注册并实名认证来获取一个七牛云账号和存 ...
- solr配置ik中文分词(二)
上一篇文章主要介绍了solr的安装与配置,这篇文章主要记录如何使用ik分词器对中文进行分词. 步骤: 1.下载ik分词jar包:ik-analyzer-solr5-5.x.jar. 2.将下载的jar ...
- python 读取csv 数据并画图分析
数据源 : https://pan.baidu.com/s/1eR593Uy 密码: yqjh python环境 python3 #encoding: utf-8 import csv impo ...