1. 程式人生 > >[NOI2011] 阿狸的打字機

[NOI2011] 阿狸的打字機

Description

給定\(N(N\leq 10^5)\)個串,\(m(m\leq 10^5)\)次詢問,每次詢問第\(x\)個串在第\(y\)個串中出現了幾次。

Solution

腦抽又調了一個小時。。。

考慮AC自動機fail指標的含義,如果 \(fail[x]=y\) 那說明以 \(y\) 結尾的串在 \(x\) 中出現了。

於是詢問 \(x\)\(y\) 中出現了幾次實際上就是問trie樹上根到 \(y\) 的路徑有多少點的 \(fail\) 指標直接或間接指向了 \(x\) 的結尾節點。可以把 \(fail\) 指標當做樹邊拎出來建樹。

於是詢問變成在新的 \(fail\) 樹上以點 \(x\)

為根的子樹中有多少點是在trie樹上根到 \(y\) 的路徑上的。

先dfs一遍 \(fail\) 樹求出dfs序轉化為序列上的問題。

直接在 \(fail\) 樹上做貌似不是很好做,我們回到原來的trie樹上,dfs 一遍整個trie樹。進入一個點就在序列上對應位置+1,退出一個點就在對應位置-1,這樣保證了只有當前路徑是有值的。如果碰到了單詞節點,就相當於我們遇到了詢問 \((x,y)\) 中的 \(y\),掃一下所有的 \(x\),查子樹和就行了。

Code

#include<set>
#include<map>
#include<cmath>
#include<queue>
#include<cctype>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using std::min;
using std::max;
using std::swap;
using std::vector;
const int M=8e5+5;
const int N=1e5+5;
typedef double db;
typedef long long ll;
#define pb(A) push_back(A)
#define pii std::pair<int,int>
#define all(A) A.begin(),A.end()
#define mp(A,B) std::make_pair(A,B)

vector< pii > v[N];
vector<int> js[M];char a[N];
int tot,head[M],ch[M][27],ans[N],fa[M];
int n,m,cnt,fs[N],fail[M],dfn[M],f[M],sze[M];

struct Edge{
    int to,nxt;
}edge[M];

void add(int x,int y){
    edge[++cnt].to=y;
    edge[cnt].nxt=head[x];
    head[x]=cnt;
}

void add(int x){
    while(x) f[x]++,x-=x&-x;
}

void clr(int x){
    while(x) f[x]--,x-=x&-x;
}

int query(int x){
    int now=0;
    while(x<=tot) now+=f[x],x+=x&-x;
    return now;
}

int getint(){
    int X=0,w=0;char ch=0;
    while(!isdigit(ch))w|=ch=='-',ch=getchar();
    while( isdigit(ch))X=X*10+ch-48,ch=getchar();
    if(w) return -X;return X;
}

void getfail(){
    std::queue<int> q;
    for(int i=0;i<26;i++)
        if(ch[0][i])
            q.push(ch[0][i]);
    while(q.size()){
        int u=q.front();q.pop();add(fail[u],u);
        for(int i=0;i<26;i++){
            if(ch[u][i]) 
                fail[ch[u][i]]=ch[fail[u]][i],q.push(ch[u][i]);
            else ch[u][i]=ch[fail[u]][i];
        }
    }
}

void dfs(int now){
    sze[now]=1;dfn[now]=++tot;
    for(int i=head[now];i;i=edge[i].nxt){
        int to=edge[i].to;
        dfs(to);sze[now]+=sze[to];
    } 
}

void sdfs(int now){
    add(dfn[now]);
    for(int x:js[now])
        for(auto y:v[x])
            ans[y.second]=query(dfn[fs[y.first]])-query(dfn[fs[y.first]]+sze[fs[y.first]]);
    for(int i=0;i<26;i++)
        if(ch[now][i])
            sdfs(ch[now][i]);
    clr(dfn[now]);
}

signed main(){
    scanf("%s",a+1);
    int len=strlen(a+1),now=0;
    for(int i=1;i<=len;i++){
        if(a[i]>='a' and a[i]<='z'){
            if(!ch[now][a[i]-'a']) ch[now][a[i]-'a']=++tot,fa[tot]=now;
            now=ch[now][a[i]-'a'];
        }
        else if(a[i]=='B')
            now=fa[now];
        else n++,js[now].pb(n),fs[n]=now;
    } tot=0;getfail();dfs(0);int cnts=0;
    memset(ch,0,sizeof ch);now=0;
    for(int i=1;i<=len;i++){
        if(a[i]>='a' and a[i]<='z'){
            if(!ch[now][a[i]-'a']) ch[now][a[i]-'a']=++cnts;
            now=ch[now][a[i]-'a'];
        }
        else if(a[i]=='B')
            now=fa[now];
    }
    m=getint();
    for(int i=1;i<=m;i++){
        int x=getint(),y=getint();
        v[y].pb(mp(x,i));
    } sdfs(0);
    for(int i=1;i<=m;i++)
        printf("%d\n",ans[i]);
    return 0;
}