码迷,mamicode.com
首页 > 其他好文 > 详细

bzoj2434 阿狸的打字机

时间:2019-02-01 17:08:47      阅读:205      评论:0      收藏:0      [点我收藏+]

标签:ret   修改   dfs   怎么   trie   出现   一个   href   name   

题目链接

思路

可以发现,其实题目中所描述的操作,就是在\(AC\)自动机上走的过程。输出就是打上标记。删除就是返回父亲节点。
然后看询问。每次询问字符串\(x\)在字符串中\(y\)出现的次数。其实也就是问在\(AC\)自动机上的\(y\)这个字符串上,有多少位置的\(fail\)指针指向\(x\)的结尾。
所以想到将\(fail\)指针反过来,建立\(fail\)树。
先考虑只有一次询问的时候怎么做。只要将\(AC\)自动机上从根到\(y\)路径上的点在\(fail\)树上对应的变成\(1\)。然后询问以\(fail\)树上以\(x\)为根的子树有多少个\(1\)
对于多次询问,可以将询问离线下来。然后在\(AC\)自动机上走。当新走到一个点的时候,就在\(fail\)树上这个点对应的\(++\)。当删除一个点的时候,就在\(fail\)树上这个点对应的\(--\)。当打印字符串的时候。就回答那些\(y\)为当前字符串的询问。
这样的单点修改,子树查询可以用\(dfs\)序+树状数组解决。

代码

/*
* @Author: wxyww
* @Date:   2019-02-01 10:28:18
* @Last Modified time: 2019-02-01 16:35:45
*/
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cmath>
#include<queue>
#include<ctime>
#include<cstring>
#include<algorithm>
#include<bitset>
using namespace std;
typedef long long ll;
const int N = 100000 + 100;
ll read() {
    ll x=0,f=1;char c=getchar();
    while(c<'0'||c>'9') {
        if(c=='-') f=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9') {
        x=x*10+c-'0';
        c=getchar();
    }
    return x*f;
}
char s[N];
int dy[N];
int trie[N][26];
int ans[N];
struct node{
    int x,y,id;
}que[N];
bool cmp(node x,node y) {
    return x.y < y.y;   
}
int tot = 1,n,now = 1;
int fa[N],bz[N],bzjs;
void solve() {
    n = strlen(s + 1);
    for(int i = 0;i <= 100010;++i) fa[i] = 1;
    for(int i = 1;i <= n;++i) {
        if(s[i] == 'P') {
            bz[now] = ++bzjs;
            dy[bzjs] = now;
            continue;
        }
        if(s[i] == 'B') {
            now = fa[now];
            continue;
        }
        int x = s[i] - 'a';
        if(!trie[now][x]) trie[now][x] = ++tot;
        fa[trie[now][x]] = now;
        now = trie[now][x];
    }
}
queue<int>q;
vector<int>e[N];
int fail[N];
void get_fail() {
    for(int i = 0;i <= tot;++i) fail[i] = 1;
    for(int i = 0;i < 26;++i) if(trie[1][i]) q.push(trie[1][i]);
    while(!q.empty()) {
        int u = q.front();q.pop();
        for(int i = 0;i < 26;++i) {
            if(trie[u][i]) fail[trie[u][i]] = trie[fail[u]][i],q.push(trie[u][i]);
            else trie[u][i] = trie[fail[u]][i];
        }
    }
    for(int i = 2;i <= tot;++i) {
        if(fail[i] == 0) fail[i] = 1;e[fail[i]].push_back(i);
    }
}
int id[N],cnt,siz[N];
void dfs(int u) {
    id[u] = ++cnt;
    siz[u] = 1;
    int k = e[u].size();
    for(int i = 0;i < k;++i) {
        int v = e[u][i];
        dfs(v);
        siz[u] += siz[v];
    }
}
int pos = 1;
int tree[N];
void update(int pos,int c) {
    while(pos <= tot) {
        tree[pos] += c;
        pos += pos & -pos;
    }
}
int query(int pos) {
    int anss = 0;
    while(pos >= 1) {
        anss += tree[pos];
        pos -= pos & -pos;
    }
    return anss;
}
void work() {
    now = 1;
    for(int i = 1;i <= n;++i) {
        if(s[i] == 'P') {
            while(que[pos].y == bz[now]) {
                ans[que[pos].id] = query(id[dy[que[pos].x]] + siz[dy[que[pos].x]] - 1) - query(id[dy[que[pos].x]] - 1);
                ++pos;
            }
            continue;
        }
        if(s[i] == 'B') {
            update(id[now],-1);
            now = fa[now];
            continue;
        }
        int x = s[i] - 'a';
        now = trie[now][x];
        update(id[now],1);
    }
}
int main() {
    scanf("%s",s + 1);
    int m = read();
    for(int i = 1;i <= m;++i) {
        que[i].id = i;
        que[i].x = read(),que[i].y = read();
    }
    sort(que + 1,que + m + 1,cmp);
    solve();
    get_fail();
    dfs(1);
    work();
    for(int i = 1;i <= m;++i) printf("%d\n",ans[i]);
    return 0;
}

/*
aPaPBbP 3 1 2 1 3 2 3

*/

bzoj2434 阿狸的打字机

标签:ret   修改   dfs   怎么   trie   出现   一个   href   name   

原文地址:https://www.cnblogs.com/wxyww/p/10346258.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!