BZOJ 2434 NOI 2011 阿狸的打字机 AC自动机构造fail树

题目大意:有一种打字机上有28个字母,分别是26个小写字母和BP,其中B代表退格,P代表换行,每一行就是一个字符串。现在给这些字符串标号,并询问x串在y串中出现过几次。

思路:这算是NOI史上最难的字符串的题了吧(动物园)。

首先按照题意不难建一个AC自动机出来,按照正常的思路,对于每一个询问都需要在AC自动机上暴力的查找。但这样时间会十分好看。

于是我们想,fail指针构成的一定是一棵树,将这颗树的DFS序搞出来的话会有非常好的性质--串中存在某个字串的一定在这个字串的子树中,也就是对应DFS序的一段区间里。这样我们吧所有询问离线,对整个trie树进行一次深搜,用fenwick来维护DFS序中的情况,顺便处理出来答案。

CODE:

#include <queue>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAX 100010
using namespace std;
#define P(a) ((a) - 'a')

struct Trie{
    Trie *son[26],*fail;
    int end;

    Trie() {
        memset(son,NULL,sizeof(son));
        fail = NULL;
    }
}mempool[MAX],*C = mempool + 1,*root = C++;

int asks;

int fenwick[MAX];
int head[MAX],total;
int next[MAX],aim[MAX];

int pos[MAX],p[MAX];
pair<int,int> range[MAX];
vector<pair<int,int> > G[MAX];
int ans[MAX];

inline void Add(int x,int y)
{
    next[++total] = head[x];
    aim[total] = y;
    head[x] = total;
}

void BuildTrie()
{
    static Trie *stack[MAX],*now = root;
    int top = 0,cnt = 0;
    char c;
    stack[++top] = root;
    while(c = getchar(),isalpha(c)) {
        if(c == 'P') {
            now->end = ++cnt;
            p[now - mempool] = cnt;
        }
        else if(c == 'B')
            now = stack[top--];
        else {
            stack[++top] = now;
            if(now->son[P(c)] == NULL)
                now->son[P(c)] = C++;
            now = now->son[P(c)];
        }
    }
}

void BuildACAutomation()
{
    static queue<Trie *> q;
    while(!q.empty())   q.pop();
    q.push(root);
    while(!q.empty()) {
        Trie *now = q.front(); q.pop();
        for(int i = 0; i < 26; ++i) {
            if(now->son[i] == NULL)  continue;
            if(now == root) {
                now->son[i]->fail = root;
                Add(root - mempool,now->son[i] - mempool);
            }
            else {
                Trie *temp = now->fail;
                while(temp != root && temp->son[i] == NULL)  temp = temp->fail;
                if(temp->son[i] != NULL) temp = temp->son[i];
                now->son[i]->fail = temp;
                Add(temp - mempool,now->son[i] - mempool);
            }
            q.push(now->son[i]);
        }
    }
}

void Pre(int x)
{
    static int k = 0;
    pos[x] = ++k;
    if(p[x])    range[p[x]].first = k;
    for(int i = head[x]; i; i = next[i])
        Pre(aim[i]);
    if(p[x])    range[p[x]].second = k;
}

inline void Fix(int x,int c)
{
    for(; x < MAX; x += x&-x)
        fenwick[x] += c;
}

inline int GetSum(int x)
{
    int re = 0;
    for(; x; x -= x&-x)
        re +=fenwick[x];
    return re;
}

void DFS(int x)
{
    Trie *now = &mempool[x];
    if(pos[x])  Fix(pos[x],1);
    for(vector<pair<int,int> >::iterator it = G[p[x]].begin(); it != G[p[x]].end(); ++it) {
        pair<int,int> now = *it;
        ans[now.second] = GetSum(range[now.first].second) - GetSum(range[now.first].first - 1);
    }
    for(int i = 0; i < 26; ++i)
        if(now->son[i] != NULL)
            DFS(now->son[i] - mempool);
    if(pos[x])  Fix(pos[x],-1);
}

int main()
{
    BuildTrie();
    BuildACAutomation();
    cin >> asks;
    for(int x,y,i = 1; i <= asks; ++i) {
        scanf("%d%d",&x,&y);
        G[y].push_back(make_pair(x,i));
    }
    Pre(1);
    DFS(1);
    for(int i = 1; i <= asks; ++i)
        printf("%d\n",ans[i]);
    return 0;
}
?

时间: 2024-10-18 05:05:11

BZOJ 2434 NOI 2011 阿狸的打字机 AC自动机构造fail树的相关文章

BZOJ 2434: [Noi2011]阿狸的打字机( AC自动机 + DFS序 + 树状数组 )

一个串a在b中出现, 那么a是b的某些前缀的后缀, 所以搞出AC自动机, 按fail反向建树, 然后查询(x, y)就是y的子树中有多少是x的前缀. 离线, 对AC自动机DFS一遍, 用dfs序+树状数组维护, DFS到的查询点就回答询问.时间复杂度O(|ACAM|+QlogQ) ------------------------------------------------------------------------------------------- #include<cstdio>

[NOI2011]阿狸的打字机 AC自动机+DFS序+树状数组

[NOI2011]阿狸的打字机 Description 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母.经阿狸研究发现,这个打字机是这样工作的: l 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最后).l 按一下印有'B'的按键,打字机凹槽中最后一个字母会消失.l 按一下印有'P'的按键,打字机会在纸上打印出凹槽中现有的所有字母并换行,但凹槽中的字母不会消失.例如,阿狸输入aPaPBbP

BZOJ 2434: [Noi2011]阿狸的打字机 [AC自动机 Fail树 树状数组 DFS序]

2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 2545  Solved: 1419[Submit][Status][Discuss] Description 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母.经阿狸研究发现,这个打字机是这样工作的:l 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最

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

[题意]阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母. 经阿狸研究发现,这个打字机是这样工作的: l 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最后). l 按一下印有'B'的按键,打字机凹槽中最后一个字母会消失. l 按一下印有'P'的按键,打字机会在纸上打印出凹槽中现有的所有字母并换行,但凹槽中的字母不会消失. 我们把纸上打印出来的字符串从1开始顺序编号,一直到n.打字机有一个非

bzoj 2434 [Noi2011]阿狸的打字机——AC自动机

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2434 dfs AC自动机,走过的点权值+1,回溯的时候权值-1:走到询问的 y 串的节点,看一下此时 x 串 fail 树子树和即可. #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int rdn() { int ret=0;bool fx=1;char ch

【BZOJ-2434】阿狸的打字机 AC自动机 + Fail树 + DFS序 + 树状数组

2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 2022  Solved: 1158[Submit][Status][Discuss] Description 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母.经阿狸研究发现,这个打字机是这样工作的:l 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最

【bzoj2434】阿狸的打字机-AC自动机+fail树+优化

http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=23083 Description 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母. 经阿狸研究发现,这个打字机是这样工作的: l 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最后). l 按一下印有'B'的按键,打字机凹槽中最后一个字母会消失. l 按一下印有'P'的

【BZOJ2434-[Noi2011]】阿狸的打字机(AC自动机(fail树)+离线+树状数组)

Description 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母. 经阿狸研究发现,这个打字机是这样工作的: l 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最后). l 按一下印有'B'的按键,打字机凹槽中最后一个字母会消失. l 按一下印有'P'的按键,打字机会在纸上打印出凹槽中现有的所有字母并换行,但凹槽中的字母不会消失. 例如,阿狸输入aPaPBbP,纸上被打印的字符如下:

[BZOJ2434]NOI2011阿狸的打字机|AC自动机|fail树|树状数组

这题真是太神了,好多实用的技巧..首先肯定是要先把每个要输出的串当模式串把自动机给建出来的,如果一个一个串复制出来再一个个插入显然非常慢...我们用在自动机上插入模式串的方法来建,初始时在0,新加一个字符就想下爬(或者新建),维护一个父亲指针,删除的时候就可以爬上去,这样就可以O(n)建出来了.. 再考虑询问的问题,每次把串拿出来再放进自动机跑一遍显然太慢..这里需要用到一个叫做fail树的东西,就是把fail指针当做边建成的一颗树..比如fail(i)=j,那么i在fail树上的父节点就是j.