bzoj 2434: [Noi2011]阿狸的打字机
来源:互联网 发布:淘宝评价晒图怎么删除 编辑:程序博客网 时间:2024/06/05 00:15
bzoj 2434: [Noi2011]阿狸的打字机
线段树,AC自动机应用
题意
初始字串为空,首先给定一系列操作序列,有三种操作:
- 在结尾加一个字符
- 在结尾删除一个字符
- 打印当前字串
然后多次询问第x个打印的字串在第y个打印的字串中出现了几次。
思路
%%%1
%%%2
操作字符串的过程实际是构建Trie的过程。新建字母相当于新加节点,打印字串相当于给标记,删除字母相当于回到爸爸节点。
因为Trie保存的是前缀,而子串相当于前缀的后缀,所以我们可以利用AC自动机的Fail指针:字符串u通过fail指针指向字符串v,表明v是u的子串。但是如果我们对于每个询问都直接处理,复杂度肯定过高。
又fail的性质是每个节点出度是1,所以反过来就是一棵树。u是v的爸爸意味着u代表的字串是v的子串。那么我们处理出fail树的dfs序,然后将询问离线按右端点排序,查询左端点的已经出现的fail树中的儿子个数即可。
维护一个线段树,对原Trie树进行遍历,每访问一个节点,就修改线段树,对线段树中该节点的DFS序起点的位置加上1。每往回走一步,就减去1。如果访问到了一个y字串的末尾节点,枚举询问中每个y串对应的x串,查询线段树中x串末尾节点从DFS序中的起始位置到结束位置的和,并记录答案。
然后。。。我他妈for(int i=0;i<strlen(s);i++)
成功把复杂度优化到了n方,然后gg了一晚上。
代码
#include <bits/stdc++.h>#define lson l,mid,rt<<1#define rson mid+1,r,rt<<1|1#define M(a,b) memset(a,b,sizeof(a))using namespace std;const int MAXN=100007;const int oo=0x3f3f3f3f;char input[MAXN];//AC自动机struct AC{ static const int maxnode=100007; static const int sigma_size=26; int ch[maxnode][sigma_size], val[maxnode], fail[maxnode], fa[maxnode]; int sz;//节点总数 void init() { sz=1;M(ch[0], 0); } inline int getidx(char c) { return c-'a'; } void build() { int u=0, n=strlen(input);int tmp=0; for(int i=0;i<n;i++) { if(input[i]=='P') val[++tmp]=u; else if(input[i]=='B') u=fa[u]; else { int c=getidx(input[i]); if(!ch[u][c]) { M(ch[sz], 0); ch[u][c]=sz; fa[sz++]=u; } u=ch[u][c]; } } } void getFail() { queue<int> q; fail[0]=0; for(int c=0;c<sigma_size;c++) { int u=ch[0][c]; if(u) { fail[u]=0;q.push(u); } } while(!q.empty()) { int fr=q.front();q.pop(); for(int c=0;c<sigma_size;c++) { int u=ch[fr][c]; if(!u) continue; q.push(u); int v=fail[fr]; while(v&&!ch[v][c]) v=fail[v]; fail[u]=ch[v][c]; } } }}ac;//线段树int stree[MAXN<<2];void pushup(int rt) { stree[rt]=stree[rt<<1]+stree[rt<<1|1]; }void build() { M(stree, 0); }void update(int pos, int v, int l, int r, int rt){ if(l==r) { stree[rt]+=v;return; } int mid=(l+r)>>1; if(pos<=mid) update(pos, v, lson); else update(pos, v, rson); pushup(rt);}int query(int L, int R, int l, int r, int rt){ if(L<=l&&r<=R) return stree[rt]; int mid=(l+r)>>1; int res=0; if(L<=mid) res+=query(L, R, lson); if(R>mid) res+=query(L, R, rson); return res;}//fail树int head[MAXN];struct Edge{ int to, ne; Edge() {} Edge(int a, int b) { to=a, ne=b; }};Edge e[MAXN];int sume=0;void addedge(int u, int v){ e[sume]=(Edge(v, head[u]));head[u]=sume; sume++;}int in[MAXN], ou[MAXN], dfsnum=0;void dfs(int u, int fa){ in[u]=++dfsnum; for(int i=head[u];~i;i=e[i].ne) { int v=e[i].to; if(v==fa) continue; dfs(v, u); } ou[u]=dfsnum;}//离线询问int hq[MAXN];struct Query{ int x, y; int ne; int res; void rd(int _i) { scanf("%d%d", &x, &y); res=0;ne=hq[y]; hq[y]=_i; }}q[MAXN];void solve(int n){ int curpos=0;int tmp=1; int len=strlen(input); for(int i=0;i<len;i++) { if(input[i]=='P') { for(int k=hq[tmp];~k;k=q[k].ne) { int nodenum=ac.val[q[k].x]; q[k].res=query(in[nodenum], ou[nodenum], 1, n, 1); } tmp++; } else if(input[i]=='B') { update(in[curpos], -1, 1, n, 1); curpos=ac.fa[curpos]; } else { curpos=ac.ch[curpos][input[i]-'a']; update(in[curpos], 1, 1, n, 1); } }}int main(){ scanf("%s", input); ac.init();sume=0;M(head, -1);build();M(hq, -1); dfsnum=0; ac.build(); ac.getFail(); for(int i=0;i<ac.sz;i++) { int u=i, v=ac.fail[i]; if(u!=v) addedge(v, u); } dfs(0, -1); int n=dfsnum; int m;scanf("%d", &m); for(int i=1;i<=m;i++) q[i].rd(i); solve(n); for(int i=1;i<=m;i++) { printf("%d\n", q[i].res); } //system("pause"); return 0;}
阅读全文
0 0
- bzoj-2434: [Noi2011]阿狸的打字机
- bzoj 2434: [Noi2011]阿狸的打字机
- BZOJ 2434 [Noi2011]阿狸的打字机
- BZOJ 2434 [Noi2011]阿狸的打字机
- bzoj 2434 [Noi2011]阿狸的打字机
- bzoj 2434: [Noi2011]阿狸的打字机
- BZOJ 2434 [Noi2011]阿狸的打字机
- bzoj 2434: [Noi2011]阿狸的打字机
- bzoj 2434: [Noi2011]阿狸的打字机
- 【AC自动机】 BZOJ 2434 [Noi2011]阿狸的打字机
- BZOJ 2434 [Noi2011] 阿狸的打字机 Fail树
- 2434: [Noi2011]阿狸的打字机
- 2434: [Noi2011]阿狸的打字机
- 阿狸的打字机 NOI2011
- [Noi2011]阿狸的打字机
- NOI2011阿狸的打字机
- NOI2011阿狸的打字机
- 【NOI2011】阿狸的打字机
- Facebook版”朋友圈”上线 为进入中国做准备?
- Linux CentOS 64位 防火墙设置常用命令
- 第16篇-JAVA 类加载与反射
- 第17篇-JAVA Annotation 注解
- 第18完结篇-JAVA XML
- bzoj 2434: [Noi2011]阿狸的打字机
- 萌新的linux之旅20
- 必须拼命,才能对得起老爸老妈的辛劳!
- 编写Java程序,将能够被13整除的前400个数字存储到数组中
- 萌新的linux之旅21
- Linked List-----25. Reverse Nodes in k-Group
- 东南大学 崇志宏:我们在折腾什么?
- FTPrep, 37 Sukodu Solver
- 并查集 [POI2007]洪水pow