【题目大意】

输入一个字符串,其中:(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. Linux中的vim实用命令 -- (转)

    VI 有2个模式.我自己定义的   1. 命令模式,一开始进去的模式.一些指定的键盘输入会产生不同的效果 2. 输入模式,在命令模式下输入冒号(:) 就可以进入输入模式.按Esc键即可退出命令模式. ...

  2. 结合BeautyEye开源UI框架实现的较美观的Java桌面程序

    BeautyJavaSwingRobot 结合BeautyEye开源UI框架实现的较美观的Java桌面程序,主要功能就是图灵机器人和一个2345网站万年历的抓取.... 挺简单而且实用的一个项目,实现 ...

  3. linux下进行base64编码解码

    1.编码 2.解码

  4. C++学习之路(六):实现一个String类

    直接贴代码吧,这段时间准备面试也正好练习了一下. class String { public: String(const char *str = ""); ~String(void ...

  5. device tree --- #interrupt-cells property

    device tree source Example1 interrupt-controller@e000e100 { ... ... #interrupt-cells = <0x1>; ...

  6. VPS性能综合测试(6):UnixBench跑分工具测试

    测试时间可能会比较长,请耐心等待.最后UnixBench会详细列出各个测试项目的得分情况,以及VPS性能的综合跑分结果 UinxBench 的使用 使用方法如下: Run [ -q | -v ] [- ...

  7. mysql远程访问cannot connect(10038) 问题解决的过程

    今天用Navicat访问虚拟机上的mysql,无法访问报cannot connect(10038). 首先看是否可以telnet,本机cmd,telnet 192.168.209.128 3306,结 ...

  8. Proxy那点事儿

    全文转载,原文地址:Proxy 那点事儿 Proxy,也就是"代理"了.意思就是,你不用去做,别人代替你去处理.比如说:赚钱方面,我就是我老婆的 Proxy:带小孩方面,我老婆就是 ...

  9. Restful Framework (三)

    目录 一.版本 二.解析器 三.序列化 四.请求数据验证 一.版本 回到顶部 程序也来越大时,可能通过版本不同做不同的处理 没用rest_framework之前,我们可以通过以下这样的方式去获取. c ...

  10. Word中截取部分内容并保存为jpg图片的方法

    private void button1_Click(object sender, EventArgs e) { var appWord = new Microsoft.Office.Interop. ...