【题目大意】

输入一个字符串,其中:(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. 为什么Javascript有设计缺陷

    1. 设计阶段过于仓促 Javascript的设计,其实只用了十天.而且,设计师是为了向公司交差,本人并不愿意这样设计(参见<Javascript诞生记>). 另一方面,这种语言的设计初衷 ...

  2. js 作用域链&内存回收&变量&闭包

    闭包主要涉及到js的几个其他的特性:作用域链,垃圾(内存)回收机制,函数嵌套,等等 一.作用域链:函数在定义的时候创建的,用于寻找使用到的变量的值的一个索引,而他内部的规则是,把函数自身的本地变量放在 ...

  3. 在使用ubuntu16.04+python3.5 下使用pip3出现pip3 error - '_NamespacePath' object has no attribute 'sort'

    使用pip3安装tensorflow以及gensim等时,出现如下错误: Traceback (most recent call last): File "/usr/local/bin/pi ...

  4. linux内存占用查看

    查看内存使用情况 free free -m //显示单位为:兆 查看占用内存最高的5个进程ps aux | sort -k4nr | head -n 5 查看占用CPU最高的5个进程ps aux |  ...

  5. 如何才可以干掉Cortana进程,开机不启动

    直接禁用即可WIN——设置——隐私——语音.墨迹书写和键入——停止收集有关我的信息——关闭

  6. linux命令行todo列表管理工具Taskwarrior介绍

    Taskwarrior 是一款在命令行下使用的TODO列表管理工具,或者说任务管理工具,灵活,快速,高效. 安装 在ubuntu 14.04 中,可从官方仓库安装task软件包 sudo apt-ge ...

  7. iOS一个项目开始创建, 部署到git服务器

    在做iOS开发时, 最开始可能你的经理部署项目, 所以你不会插手, 只是直接从git上clone下来然后就开始撸码, 如果有一天你做经理了, 你怎么去部署一个项目呢, 下面我来过一遍流程 1. 首先需 ...

  8. python 结束练习

    1.文件操作有哪些模式?请简述各模式的作用 r 只读模式 r+ 读写 rb w 只写模式 w+ 写读 wb x 只写模式 x+ 写读 xb a 追加模式 a+ 写读 ab 2.s = '**hello ...

  9. hdu 1044(bfs+dfs+剪枝)

    Collect More Jewels Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Othe ...

  10. 关于在ASP.NET MVC 中使用EF的Code First的方式来读取数据库时的Validation failed for one or more entities. See 'EntityValidationErrors' property for more details.

    今天在做一个小网站的时候遇到很多问题唉,我还是个菜鸟,懂的也不多,今天一个表单的提交按钮用不了,都弄了几个小时唉.不过最后还是搞定了,还有浏览器有开发人员选项,不然我都不知道我还要继续排查多久哦,今天 ...