转载自 http://www.cnblogs.com/zj75211/p/6934976.html

●BZOJ 2434: [Noi2011]阿狸的打字机

 

●赘述题目

(题意就不赘述了)

●解法:

●我先想的一个比较暴力的方法(要TLE):

(ac自动机)先求出last数组(参见刘汝佳的解释:last[j]:表示j节点沿着失配指针往回走时,遇到的下一个单词节点(即单词在此结束)的编号),然后对输入的编号为y的字符串的每一个位置进行递归寻找是否能连上x字符串的结束节点。(给出失败代码片段图,就不解释了)

●正解:

(ac自动机)求出fail数组,然后以fail数组建树,如图

(看啊,红色的边和各点形成了另一棵树)

那么(看红树),若一个点在某个字符串结束节点的子树内,那么该字符串则出现在那个点所在的字符串里;如图中的a-b-c字符串和c字符串。

现在,我们若要求x字符串在y内出现了几次,就只需求以x的结束节点为根的子树内,有多少个节点是y字符串上的。

如何做呢?

将询问离线,y相同询问的弄在一起;

然后求出红树的dfs序(有点诡异,看代码);

我们再遍历一遍输入的字符串:

对于输入的‘a’-‘z’,把对应的dfs序中其出现的位置的值加1,用树状数组维护;

对于输入的‘B’,现在的字符所对应的dfs序中的位置的值减1;

对于遇到的c个‘P’,我们不难发现,现在的树状树状维护的便是第c个字符串的每一个字符在dfs序中的位置的值所加1后的结果。接着便可用区间查询求出y==c的询问的答案。

那么上代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#include<iostream>
using namespace std;
struct node{
    int x,y;
} q[100005];
struct edge{
    int to,next;
}e[200005];
int ch[100005][27];
int val[100005],fail[100005],fa[100005],fini[100005],l[100005],r[100005],ans[100005];
int head[100005],headq[100005],nxt[100005],lat[100005],c[150000];
char x[100005];
int cnt,pnt,ent=1,dnt,lx;
int idx(char x) {return x-'a';}
void modify(int u,int d) {for(int i=u;i<=dnt;i+=i&(-i)) c[i]+=d;}
int query(int u) {int sum=0;for(int i=u;i;i-=i&(-i)) sum+=c[i]; return sum;}
void add(int u,int v)
{
    e[ent]=(edge){v,head[u]};head[u]=ent++;
    e[ent]=(edge){u,head[v]};head[v]=ent++;
}
void read_trie()
{
    int u=0;
    for(int i=1;i<=lx;i++)
    {
        if(x[i]=='B') u=fa[u];
        else if(x[i]=='P') val[u]=++pnt,fini[pnt]=u;
        else
        {
            int c=idx(x[i]);
            if(!ch[u][c]) ch[u][c]=++cnt,fa[ch[u][c]]=u;
            u=ch[u][c];
        }
    }
}
void get_fail()
{
    queue<int> q;
    for(int c=0;c<26;c++) {int u=ch[0][c]; if(u) q.push(u);}
    while(!q.empty())
    {
        int r=q.front(); q.pop();
        for(int c=0;c<26;c++)
        {
            if(!ch[r][c]) continue;
            int u=ch[r][c];
            q.push(u);
            int v=fail[r];
            while(v&&!ch[v][c]) v=fail[v];
            fail[u]=ch[v][c];
        }
    }
}
//----------------------------------------------------------------------
void dfs_xu(int u,int fa)
{
    l[u]=++dnt;
    for(int i=head[u];i;i=e[i].next) if(e[i].to!=fa) dfs_xu(e[i].to,u);
    r[u]=dnt;
}
void work()
{
    int m; scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&q[i].x,&q[i].y); 
        nxt[i]=lat[q[i].y];
        lat[q[i].y]=i;
    }
    for(int i=1;i<=cnt;i++) add(i,fail[i]);
    dfs_xu(0,0);
    int p=0,id=0;
    for(int i=1;i<=lx;i++)
    {
        if (x[i]=='P')
        {
            id++;
            for (int j=lat[id];j;j=nxt[j])
            
                int u=fini[q[j].x]; 
                ans[j]=query(r[u])-query(l[u]-1); 
            
        
        else if (x[i]=='B') modify(l[p],-1),p=fa[p]; 
        else p=ch[p][idx(x[i])],modify(l[p],1); 
    }
    for(int i=1;i<=m;i++) printf("%d\n",ans[i]);
}
int main()
{
    scanf("%s",x+1);
    lx=strlen(x+1);
    read_trie();
    get_fail();
    work();
    return 0;
}

bzoj2434阿狸的自动机的更多相关文章

  1. [NOI2011][bzoj2434] 阿狸的打字机 [AC自动机+dfs序+fail树+树状数组]

    题面 传送门 正文 最暴力的 最暴力的方法:把所有询问代表的字符串跑一遍kmp然后输出 稍微优化一下:把所有询问保存起来,把模板串相同的合并,求出next然后匹配 但是这两种方法本质没有区别,都是暴力 ...

  2. bzoj2434 阿狸的打字机

    题目链接 思路 可以发现,其实题目中所描述的操作,就是在\(AC\)自动机上走的过程.输出就是打上标记.删除就是返回父亲节点. 然后看询问.每次询问字符串\(x\)在字符串中\(y\)出现的次数.其实 ...

  3. 【字符串】BZOJ上面几个AC自动机求最为字串出现次数的题目

    (一下只供自己复习用,目的是对比这几个题,所以写得不详细.需要细节的可以参考其他博主) [BZOJ3172:单词] 题目: 某人读论文,一篇论文是由许多(N)单词组成.但他发现一个单词会在论文中出现很 ...

  4. AC自动机题单

    AC自动机题目 真的超级感谢xzy 真的帮到我很多 题单 [X] [luogu3808][模板]AC自动机(简单版) https://www.luogu.org/problemnew/show/P38 ...

  5. fail树

    前置技能:AC自动机 假设我们有了一个AC自动机,然后在上面进行字符串匹配. 上面是一个有四个字符串的AC自动机(abcde.aacdf.cdf.cde),虚线是fail指针,实线是转移. 这是上一次 ...

  6. 【BZOJ2434】阿狸的打字机(AC自动机,树状数组)

    [BZOJ2434]阿狸的打字机(AC自动机,树状数组) 先写个暴力: 每次打印出字符串后,就插入到\(Trie\)树中 搞完后直接搭\(AC\)自动机 看一看匹配是怎么样的: 每次沿着\(AC\)自 ...

  7. 【BZOJ2434】【NOI2011】阿狸的打字机(AC自动机,树状数组)

    [BZOJ2434]阿狸的打字机(AC自动机,树状数组) 先写个暴力: 每次打印出字符串后,就插入到\(Trie\)树中 搞完后直接搭\(AC\)自动机 看一看匹配是怎么样的: 每次沿着\(AC\)自 ...

  8. 【BZOJ2434】[NOI2011]阿狸的打字机 AC自动机+DFS序+树状数组

    [BZOJ2434][NOI2011]阿狸的打字机 Description 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P ...

  9. 【bzoj2434】: [Noi2011]阿狸的打字机 字符串-AC自动机-BIT

    [bzoj2434]: [Noi2011]阿狸的打字机 x串在y串上的匹配次数就是y在自动机所有节点上能够通过fail走到x最后一个节点的个数 (就是y串任意一个前缀的后缀能匹配到x的个数)和[bzo ...

随机推荐

  1. DML数据操作语言之查询(二)

    当我们查询出了N条记录之后 ,我们知道一共是几条记录,或者这些记录某一字段(列值)的最大值,最小值,平均值等,就可以使用聚合函数. 1.聚合函数 聚合函数会将null 排除在外.但是count(*)例 ...

  2. zookeeper 入门系列-理论基础 – zab 协议

    上一章讨论了paxos算法,把paxos推到一个很高的位置.但是,paxos有没有什么问题呢?实际上,paxos还是有其自身的缺点的: 1. 活锁问题.在base-paxos算法中,不存在leader ...

  3. python 学习笔记

    1. 关于两者详细解释,参考链接:www.crifan.com/python_re_search_vs_re_findall/ 代码图

  4. clang++ 链接问题 和 VS Code

    clang++ 链接问题 和 VS Code 如果你在windows上使用clang 并且同时安装有vs和mingw, clang链接是会自动使用msvs, 链接时会有LINK error LINK ...

  5. Python内置函数(42)——hash

    英文文档: hash(object)Return the hash value of the object (if it has one). Hash values are integers. The ...

  6. Python内置函数(3)——max

    英文文档: max(iterable, *[, key, default]) max(arg1, arg2, *args[, key]) Return the largest item in an i ...

  7. 原生Ajax用法——一个简单的实例

    Ajax全名(Asynchronous(异步) JavaScript and XML )是可以实现局部刷新的 在讲AJax之前我们先用简单的实例说一下同步和异步这个概念 /*异步的概念(就是当领导有一 ...

  8. Zookeeper分布式服务协调组件

    1.简介 Zookeeper是一个分布式服务协调组件,是Hadoop.Hbase.Kafka的重要组件,它是一个为分布式应用提供一致性服务的组件.   Zookeeper的目标就是封装好复杂易出错的服 ...

  9. Django 框架介绍

    Django 框架介绍 MVC框架和MTV框架 简单了解一下什么是MVC框架.MVC(Model View Controller),是模型(model)-视图(view)-控制器(controller ...

  10. JavaScript 克隆

    JavaScript 克隆 本次学习内容: 克隆:只克隆标签和属性,不克隆文本. 克隆的功能,如果不添加使用Ture,就只会克隆标签和属性,不会克隆文本. 克隆的参数全部是节点对象,不能是字符串 &l ...