P2414 NOI2011阿狸的打字機 [AC自動機,dfs序]

阿狸的打字機

題解

題目中給出的字符串就是構建TrieTrie樹的順序.我們將字符串依次讀入,每讀入一個小寫字符就相當於在TrieTrie樹當前節點下插入一個小寫字符,讀入BB時,就在TrieTrie樹中向父節點移動一步.讀入PP的時候,就做一個標記.

然後對這顆TrieTrie樹構建ACAC自動機.

找找規律發現第xx串在第yy串中出現的次數就是TrieTrie樹上xx串尾點到yy串末尾點的樹鏈上,所有能直接或間接通過failfail指針跳到xx串末尾點的點的個數.

觀察到自動機上failfail指針構成了一顆failfail樹.

進一步我們發現,所有能直接或間接跳到xx串末尾點的點就是failfail樹上,xx節點的子樹大小.

而實際的答案就是failfail樹上xx串末尾點的子樹與TrieTrie樹上xxyy的樹鏈的交集中點的個數.

我們對failfail樹做一遍dfsdfs序,用這個dfsdfs序就可以查詢和維護failfail樹的子樹的信息了.

對這個dfsdfs序列建立一個樹狀數組.

然後我們對TrieTrie樹做一遍dfsdfs,在dfsdfs的過程中,把當前點在樹狀數組中的dfndfn位置+1+1,返回的時候在該位置1-1,這樣相當於在TrieTrie樹中從當前點到根節點的樹鏈上所有節點都已經在樹狀數組中激活了,對於當前點作爲yy的詢問,在failfail樹的以xx爲根的子樹中計算有多少個點被激活就ok了(樹狀數組查詢前綴和).

代碼

#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#include <vector>
#define pr(x) std::cout << #x << ':' << x << std::endl
#define rep(i,a,b) for(int i = a;i <= b;++i)
#define LETTER 26
typedef std::pair<int,int> pii;
const int N = 100010;
char s[N];
struct Trie{
    int num,fail,fa;
    int next[LETTER];
}pool[N];
Trie* const trie = pool + 1;
int cnt;
void init() {
    cnt = 0;
    memset(pool,0,2*sizeof(Trie));
    trie[0].fail = -1;
}
int now;
int pat[N];
int pc;
std::vector<int> trie_edge[N];
void build() {
    pc = now = 0;
    for(int i = 0;s[i];++i) {
        if(s[i] == 'B') 
            now = trie[now].fa;
        else if(s[i] == 'P') 
            pat[++pc] = now;
        else {
            int &pos = trie[now].next[s[i]-'a'];
            if(!pos) {
                pos = ++cnt;
                memset(&trie[pos],0,sizeof(Trie));
                trie[pos].fa = now;
                trie_edge[now].push_back(pos);
            }
            now = pos;
        }
    }
    std::queue<int> Q;
    Q.push(0);
    while(!Q.empty()) {
        int t = Q.front();Q.pop();
        for(int i = 0;i < LETTER;++i) {
            int &cur = trie[t].next[i];
            if(cur) {
                Q.push(cur);
                trie[cur].fail = trie[trie[t].fail].next[i];
            }
            else 
                cur = trie[trie[t].fail].next[i];
        }
    }
}
int bitree[N<<1];
int lowbit(int x) {return x & (-x);}
int sum(int pos) {
    int res = 0;
    for(;pos;pos -= lowbit(pos)) 
        res += bitree[pos];
    return res;
}
void add(int pos,int x) {
    for(;pos < N<<1;pos += lowbit(pos))
        bitree[pos] += x;
}

std::vector<int> edge[N];
int beg[N],end[N];
int idx = 0;
void dfs1(int u){
    beg[u] = ++idx;
    for(int v : edge[u]) 
        dfs1(v);
    end[u] = ++idx;
}
std::vector<pii> query[N];
int ans[N];
void dfs2(int u) {
    add(beg[u],1);
    for(pii p : query[u]) {
        int x = p.first,id = p.second;
        ans[id] = sum(end[x])-sum(beg[x]-1);
    }
    for(int v : trie_edge[u])
        dfs2(v);
    add(beg[u],-1);
}

int main() {
    init();
    std::ios::sync_with_stdio(false);
    std::cin >> s;
    build();
    for(int i = 1;i <= cnt;++i) edge[trie[i].fail].push_back(i);
    dfs1(0);
    int m;
    std::cin >> m;
    for(int i = 1;i <= m;++i) {
        int x,y;
        std::cin >> x >> y;
        query[pat[y]].push_back((pii){pat[x],i});
    }
    dfs2(0);

    for(int i = 1;i <= m;++i) {
        std::cout << ans[i] << std::endl;
    }
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章