【题目大意】

输入一个字符串,其中:(1)a..z:在字符串末尾添加当前字符(2)P:输出当前字符串(3)B:从当前字符串末尾删去一个字符。

给出m组查询,输出第i个输出的字符串在第j个输出的字符串内出现了几次。

【思路】

卡了好久,写完不想调试,调试完不想提交,期间颓颓颓地弄了下博客的界面,弄成了粉嫩少女风(划掉)。结果提交完1A有点迷醉迷醉的……

首先我们要借用BZOJ3172的结论:←戳这里,这个结论66666,是本次解题的关键。

“建立AC自动机,对于路径上的每一个点sum++,表示出现的次数。fail指针指向的后缀,如果从fail指针指向的点开始方向建立fail树,其子树的sum之和就等于以它作为后缀的串的总数,相当于它在文章中出现的个数。”

显然我们要把m组询问(i,j)调换位置,同一组j,对i建立一个链表存放。为什么要这么做呢?先继续往下看。

首先,我们建立一个AC自动机,并建立fail树。这里和一般的建立有点区别,因为有删除操作,可以用一个栈来维护当前位于Trie的位置,如果B删除就弹出栈顶,如果为字母就建立新节点并压入栈中,如果是P就在当前位置打上ed的标记,表示这是第ed次打印的字符终止位置。

由于树中节点和子树的dfs序是连续的,我们跑一次dfs记录下fail树上每个点的dfs序,记录下第i个输出字符串,它的末尾节点及其子树在dfs序中的start和end位置。

最后,重新跑一次Trie,和先前一样用栈维护。如果遇到B,就将当前节点-1,弹出栈顶;遇到字母就将当前节点+1;如果遇到P,说明现在恰巧输出了第j个字符串,枚举每一个查询中的i,查询结果=当前i对应的节点和它子树之和。

没错,这是一个单点修改,区间求和,可以用树状数组来维护一下,每个节点在树状数组中的位置就等于它的dfs序。那么当前i对应的节点及其子树之和=sum[start[i]..end[i]],搞定!

 #include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#define lnum 26
using namespace std;
const int MAXN=+;
int ppos[MAXN];//第i个P对应的栈顶位置编号
char str[MAXN];
int m,len,step,cnt=,e[MAXN],start[MAXN],end[MAXN],ans[MAXN];
struct ACauto
{
ACauto* next[lnum];
ACauto* fail;
int id,ed,order;
ACauto()
{
for (int i=;i<lnum;i++) next[i]=NULL;
fail=NULL;
id=++cnt;
}
};
struct node
{
int to,id;
};
ACauto* rt=new ACauto();
ACauto* p[MAXN];//编号为i的节点指向的位置
vector<node> Q[MAXN];
vector<int> E[MAXN]; void addedge(int u,int v)
{
E[u].push_back(v);
} int lowbit(int x)
{
return (x&(-x));
} void modify(int p,int x)
{
int i=p;
while (i<=len)
{
e[i]+=x;
i+=lowbit(i);
}
} int sum(int p)
{
int s=;
int i=p;
while (i>)
{
s+=e[i];
i-=lowbit(i);
}
return s;
} void init()
{
scanf("%s",str);
len=strlen(str);
int top=,n=;
ACauto* stack[MAXN];
stack[++top]=p[]=rt;//不要忘记给p[1]赋值
for (int i=;i<len;i++)
{
if (str[i]=='P')
{
stack[top]->ed=++n;
ppos[n]=stack[top]->id;
}
else if (str[i]=='B') top--;
else
{
int index=str[i]-'a';
if (stack[top]->next[index]==NULL)
{
stack[top]->next[index]=new ACauto();
p[cnt]=stack[top]->next[index];
}
stack[++top]=stack[top-]->next[index];
}
} scanf("%d",&m);
for (int i=;i<m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
Q[y].push_back((node){ppos[x],i});
}
} void buildfail()
{
queue<ACauto*> que;
que.push(rt);
while (!que.empty())
{
ACauto* head=que.front();que.pop();
for (int i=;i<lnum;i++)
{
if (head->next[i]!=NULL)
{
if (head==rt)
{
head->next[i]->fail=rt;
addedge(rt->id,head->next[i]->id);
}
else
{
ACauto* tmp=head->fail;
while (tmp!=NULL)
{
if (tmp->next[i]!=NULL)
{
head->next[i]->fail=tmp->next[i];
addedge(tmp->next[i]->id,head->next[i]->id);
break;
}
else tmp=tmp->fail;
}
if (tmp==NULL)
{
head->next[i]->fail=rt;
addedge(rt->id,head->next[i]->id);
}
}
que.push(head->next[i]);
}
}
}
} void dfs(int pos)
{
ACauto* tmp=p[pos];
tmp->order=++step;
if (tmp->ed) start[tmp->ed]=step;
for (int i=;i<E[pos].size();i++) dfs(E[pos][i]);
if (tmp->ed) end[tmp->ed]=step;
} void query()
{
memset(e,,sizeof(e));
int top=,n=;
ACauto* stack[MAXN];
stack[++top]=rt;
for (int i=;i<len;i++)
{
if (str[i]=='P')
{
n++;
for (int i=;i<Q[n].size();i++)
{
ACauto* to=p[Q[n][i].to];
ans[Q[n][i].id]=sum(end[to->ed])-sum(start[to->ed]-);
}
}
else if (str[i]=='B')
{
modify(stack[top]->order,-);
top--;
}
else
{
int index=str[i]-'a';
stack[++top]=stack[top-]->next[index];
modify(stack[top]->order,);
}
}
} void printans()
{
for (int i=;i<m;i++) printf("%d\n",ans[i]);
} int main()
{
init();
buildfail();
dfs(rt->id);
query();
printans();
return ;
}

【dfs序+AC自动机+树状数组】BZOJ2434-[Noi2011]阿狸的打字机的更多相关文章

  1. 【BZOJ】2434: [Noi2011]阿狸的打字机 AC自动机+树状数组+DFS序

    [题意]阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母. 经阿狸研究发现,这个打字机是这样工作的: l 输入小写 ...

  2. BZOJ.2434.[NOI2011]阿狸的打字机(AC自动机 树状数组 DFS序)

    题目链接 首先不需要存储每个字符串,可以将所有输入的字符依次存进Trie树,对于每个'P',记录该串结束的位置在哪,以及当前节点对应的是第几个串(当前串即根节点到当前节点):对于'B',只需向上跳一个 ...

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

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

  4. BZOJ3881[Coci2015]Divljak——AC自动机+树状数组+LCA+dfs序+树链的并

    题目描述 Alice有n个字符串S_1,S_2...S_n,Bob有一个字符串集合T,一开始集合是空的. 接下来会发生q个操作,操作有两种形式: “1 P”,Bob往自己的集合里添加了一个字符串P. ...

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

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

  6. CoderForces 163E e-Government(AC自动机+树状数组维护fail树的dfs序)

    E. e-Government time limit per test 1 second memory limit per test 256 megabytes input standard inpu ...

  7. BZOJ2434: [Noi2011]阿狸的打字机(AC自动机 树状数组)

    Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 4140  Solved: 2276[Submit][Status][Discuss] Descript ...

  8. [BZOJ2434][Noi2011]阿狸的打字机 AC自动机+树状数组+离线

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2434 题目中这种多个串匹配的问题,一下子就想到了AC自动机.然后发现如果要建立AC自动机, ...

  9. HDU 6096 String(AC自动机+树状数组)

    题意 给定 \(n\) 个单词,\(q\) 个询问,每个询问包含两个串 \(s_1,s_2\),询问有多少个单词以 \(s_1\) 为前缀, \(s_2\) 为后缀,前后缀不能重叠. \(1 \leq ...

随机推荐

  1. MSSQL 错误:在将 varchar 值 '1,2,3,5,6' 转换成数据类型 int 时失败。

    MSSQL  错误:在将 varchar 值 '1,2,3,5,6' 转换成数据类型 int 时失败.

  2. ms17-010 攻击win7漏洞复现

    只是为了好玩重新写一篇.利用还是很简单的. 将下载下来的rb放置在:/usr/share/metasploit-framework/modules/exploits/windows/smb/ 目录下 ...

  3. js/jq 键盘上下左右回车按键

    js判断上下左右回车按键: document.onkeydown=function(e){ e=window.event||e; switch(e.keyCode){ case 37: //左键 co ...

  4. Python模块学习 - ConfigParser

    配置文件 很多软件都用到了配置文件,像git运行的时候会读取~/gitconfig,MySQL运行的时候会读取/etc/my.cnf,Python 提供的包管理工具pip命令,也会去读取~/.pip/ ...

  5. 解决linux下终端无法输入的假死问题

    有时在linux下shell终端中,会突然出现终端应用卡死,无法接受键盘输入, 但是其它分屏, 系统都是正常的.这本来是一个终端的很老的功能, 叫软件流控制(XON/XOFF flow control ...

  6. USB 3.1 與 USB Type-C 解釋

    https://tw.transcend-info.com/Support/FAQ-940 以下的內容皆來自上面這個網址. 什麼是USB 3.1? 什麼是USB 3.1? USB 3.1為USB協會制 ...

  7. java===java基础学习(14)---封装

    package dog; public class Demo4 { public static void main(String []args) { Worker w1= new Worker(&qu ...

  8. 中断中处理延时及一些函数的调用规则(中断调i2c驱动有感)--中断中的延迟delay与printk函数的冲突【转】

    转自:http://blog.csdn.net/psvoldemort/article/details/8222371 1,中断处理程序中不能使用有睡眠功能的函数,如ioremap,kmalloc,m ...

  9. spark 环境搭建坑

    spark的新人会有什么坑 spark是一个以java为基础的,以Scala实现的,所以在你在安装指定版本的spark,需要检查你用的是对应spark使用什么版本的scala,可以通过spark-sh ...

  10. 《java并发编程实战》读书笔记1--线程安全性,内置锁,重入,状态

    什么是线程安全? 当多个线程访问某个类时,不管这些的线程的执行顺序如何,并且在主调代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么就称这个类是线程安全的. 哈哈书上的解释,还是翻译过 ...