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

Description
给出一个串,然后这个串有28种字符,26个小写英文字母和’B’,’P’两个字母
我们定义一个模式串(一开始这个串是空的)。
假如当前输入的字符是小写英文字母的话,就在模式串的末尾加上这个字母
如果是’B’,就把模式串的末尾给去掉。
如果是’P’,就把当前模式串当作一个新的字符串。
然后给出m个询问,每个询问输入x,y,输出第x个字符串在第y个字符串中出现的次数。


Sample Input
aPaPBbP
3
1 2
1 3
2 3


Sample Output
2
1
0


通过这道题学习了一下fail树,这个结构挺好懂的。
fail树是AC自动机的延伸,你将每个点的fail与这个点连一条边,建出一棵树,那么对于一个节点,它子树里的节点的后缀就都会与这个串相同。
那我们子串其实是一个前缀的后缀,我们采用一个离线的做法,先建好一棵fail树,然后将右端点排一下序,然后每个点的查询就查询这个点的子树的和就好了,当我们建到一个的右端点,就直接树状数组维护一下区间和即可。


#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
int _max(int x, int y) {return x > y ? x : y;}
const int maxn = 210000;

struct edge {
    int x, y, next;
} e[maxn]; int len, last[maxn];
struct trnode {
    int pp, s, f, fail, v[30];
    trnode() {memset(v, -1, sizeof(v));}
} t[maxn]; int cnt, list[maxn];
struct node {
    int x, y, p;
} a[maxn];
int ans[maxn];
int id, s[maxn], cc[maxn], ll[maxn], rr[maxn];
char s1[maxn], ss[maxn];

bool cmp(node a, node b) {return a.y < b.y;}

void ins(int x, int y) {
    e[++len].x = x; e[len].y = y;
    e[len].next = last[x]; last[x] = len;
}

void get_fail() {
    int head = 1, tail = 2;
    list[1] = 0;
    while(head != tail) {
        int x = list[head];
        for(int i = 1; i <= 26; i++) {
            int y = t[x].v[i];
            if(y == -1) continue;
            if(x == 0) t[y].fail = 0;
            else {
                int j = t[x].fail;
                while(j && t[j].v[i] == -1) j = t[j].fail;
                t[y].fail = _max(0, t[j].v[i]);
            }
            list[tail++] = y;
        }
        ins(t[x].fail, x);
        head++;
    }
}

int lowbit(int x) {return x & -x;}
void change(int x, int c) {
    for(int i = x; i <= id; i += lowbit(i)) s[i] += c;
}
int getsum(int x) {
    int sum = 0;
    for(int i = x; i >= 1; i -= lowbit(i)) sum += s[i];
    return sum;
}

void dfs(int x) {
    ll[x] = ++id;
    for(int k = last[x]; k; k = e[k].next) {
        int y = e[k].y;
        if(x != y) dfs(y);
    }
    rr[x] = id;
}

int main() {
    scanf("%s", s1 + 1);
    int n = strlen(s1 + 1);
    int uu = 0, oo = 0, x = 0;
    for(int i = 1; i <= n; i++) {
        if(s1[i] == 'P') {uu++; t[x].s++; cc[uu] = x;}
        else if(s1[i] == 'B') x = t[x].f;
        else {
            int tt = s1[i] - 'a' + 1;
            if(t[x].v[tt] == -1) t[x].v[tt] = ++cnt;
            t[x].s++; t[t[x].v[tt]].f = x; x = t[x].v[tt];
        }
    }
    get_fail();
    dfs(0);
    int now = 0;
    int m; scanf("%d", &m);
    for(int i = 1; i <= m; i++) scanf("%d%d", &a[i].x, &a[i].y), a[i].p = i;
    sort(a + 1, a + m + 1, cmp);
    x = 0; int pp = 1;
    uu = 0, oo = 0;
    for(int i = 1; i <= n; i++) {
        if(s1[i] == 'P') {
            uu++;
            while(a[pp].y == uu) {
                ans[a[pp].p] = getsum(rr[cc[a[pp].x]]) - getsum(ll[cc[a[pp].x]] - 1);
                pp++;
            }
        }
        else if(s1[i] == 'B') {
            change(ll[x], -1);
            x = t[x].f;
        }
        else {
            int tt = s1[i] - 'a' + 1;
            x = t[x].v[tt];
            change(ll[x], 1);
        }
    }
    for(int i = 1; i <= m; i++) printf("%d\n", ans[i]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/xgc_woker/article/details/80064833