首页 > 编程语言 > 详细

「NOI2011」阿狸的打字机——AC自动机+树状数组

时间:2019-07-15 19:24:09      阅读:60      评论:0      收藏:0      [点我收藏+]

题面

  LOJ #2444.

解析

  一个串在另一个串中出现了几次,很容易联想到AC自动机,但暴力匹配肯定是不行的,需要我们巧妙地维护

  我们发现模式串在文本串中的出现次数就是文本串在trie树上包含的所有节点中,不断跳tail指针可以跳到模式串的终止节点的个数

  显然不能暴力跳,由于每个节点有且只有一个fail指针指向的点,因此fail指针构成了一颗树,可以跳到模式串终止节点的点在fail树中模式串终止节点的子树内,求一个子树内有多少打了标记的点就是求一段区间内的有多少打了标记的点,因此我们先在fail树上得出dfs序,在dfs序上维护一个树状数组,我们就可以方便地维护区间了,但同时如果每次查询都遍历一遍trie树的话,那就一定爆炸了,其实只要离线处理查询,在进入子树时区间+1,离开时区间-1就行了

  注意建自动机时可以优化常数,没有必要每次都从根节点开始跳, 可以每读进一个字符就跳一次now,这样常数就不会炸

  还有一点个人经历,就是f数组(存节点的父亲,方便删除节点)要开int,我结果还犯了开错类型,开成char了,自己调了1个小时,小数据全过,大数据崩盘,最后还是j神帮我看出的错

  代码:

  

技术分享图片
#include<bits/stdc++.h>
using namespace std;
const int maxn = 100005;

int cnt, n, s[maxn*4][30], num[maxn], tot, p, top, ans[maxn], fail[maxn*4], v[maxn*4][30];
char c[maxn], f[maxn*4];

vector<int> H[maxn*4];
vector<pair<int, int> > G[maxn*4];

queue<int> q;

void getfail()
{
    for(int i = 0; i < 26; ++i)
        if(s[0][i])
            q.push(s[0][i]);
    while(!q.empty())
    {
        int st = q.front();
        q.pop();
        for(int i = 0; i < 26; ++i)
            if(s[st][i])
            {
                q.push(s[st][i]);
                int t = fail[st];
                while(t && !s[t][i])    t = fail[t];
                fail[s[st][i]] = s[t][i];
            }
            else
                s[st][i] = s[fail[st]][i];
    }
}

int tr[maxn*4], siz[maxn*4], pos[maxn*4];

void dfs1(int x)
{
    pos[x] = ++p;
    siz[x] = 1;
    for(unsigned int i = 0; i < H[x].size(); ++i)
    {
        int id = H[x][i];
        dfs1(id);
        siz[x] += siz[id];
    }
}

int lowbit(int x)
{
    return x & -x;
}

int query(int x)
{
    int ret = 0;
    while(x)
    {
        ret += tr[x];
        x -= lowbit(x);
    }
    return ret;
}

void modify(int x, int v)
{
    while(x <= tot)
    {
        tr[x] += v;
        x += lowbit(x);
    }
}

void dfs2(int x)
{
    modify(pos[x], 1);
    for(int i = 0; i < 26; ++i)
        if(v[x][i])
            dfs2(v[x][i]);
    for(unsigned int i = 0; i < G[x].size(); ++i)
    {
        int id = G[x][i].first;
        ans[G[x][i].second] = query(pos[id] + siz[id] - 1) - query(pos[id] - 1);
    }
    modify(pos[x], -1);
}

int main()
{
    scanf("%s", c);
    n = strlen(c);
    int now = 0;
    for(int i = 0; i <= n; ++i)
    {
        if(c[i] >= a && c[i] <= z)
        {
            if(!s[now][c[i]-a])
            {
                s[now][c[i]-a] = ++tot;
                f[tot] = now;
            }
            now = s[now][c[i]-a];
        }
        else if(c[i] == B)
            now = f[now];
        else
            num[++cnt] = now;
    }
    for(int i = 0; i <= tot; ++i)
        for(int j = 0; j < 26; ++j)
            v[i][j] = s[i][j];
    getfail();
    int q;
    scanf("%d", &q);
    for(int i = 1; i <= q; ++i)
    {
        int x, y;
        scanf("%d%d", &x, &y);
        G[num[y]].push_back(make_pair(num[x], i));
    }
    for(int i = 1; i <= tot; ++i)
        H[fail[i]].push_back(i);
    dfs1(0);
    dfs2(0);
    for(int i = 1; i <= q ; ++i)
        printf("%d\n", ans[i]);
    return 0;
}
type

 

「NOI2011」阿狸的打字机——AC自动机+树状数组

原文:https://www.cnblogs.com/Joker-Yza/p/11187232.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!