NOI 2011 阿狸的打字機 (AC自動機+dfs序+樹狀數組)

題目大意:略(太長了不好描述) 良心LOJ傳送門

先對所有被打印的字符串建一顆Trie樹

觀察數據範圍,並不能每次打印都從頭到尾暴力建樹,而是每遍歷到一個字符就在Trie上插入這個字符,然後記錄每次打印後字符串最後一個字符在Trie樹上的位置

 

然後建立AC自動機,再建立Fail樹。注意還要另外存一下原來Trie樹的結構

Fail樹就是把Fail指針倒着跑,因爲每個點只有一個Fail指針,所以最後所有Fail指針會形成一棵樹

Fail樹有一個神奇的性質,即父節點表示的字符串(從Trie樹的根節點一直到這個點所表示的字符串),一定是子節點表示的字符串的一個後綴

原問題是求x號字符串在y號字符串內出現的次數

問題可以轉化爲,求x作爲後綴,出現在y的所有前綴的次數

根據Fail樹的性質:父節點一定是子節點的後綴

那麼從Trie根節點到y結尾節點的所有點,都能分別表示y的一個前綴串

而如果x是某個串的後綴,那麼這個串一定在x的Fail樹的子樹內

 

如果對所有問題暴力匹配x,y又不可取

查詢y的所有前綴出現在x子樹內的次數,相當於在x子樹內求和,貌似可以用DFS序+樹狀數組優化

我們可以對問題進行離線處理,把問題掛在Trie樹上以y的結尾的位置上。然後跑出Fail樹DFS序。再跑DFS,搜索這個節點,就在它dfs序入棧的位置上+1,回溯過這個節點,就-1,這個操作其實是在表示y的所有前綴

再回溯過這個節點之前,處理掛在這個點上所有的詢問。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#define idx(x) x-'a'+1
#define lowbit(x) (x&(-x))
#define N 100100
using namespace std;

char str[N];
int n,m,len,num,cte,ctq,tot,dfn,qs;
int use[N],pos[N],head[N],st[N],ed[N],s[N*2],hq[N];
struct Edge{int to,nxt;}edge[N*2],ques[N];
void ae(int u,int v){cte++;edge[cte].to=v,edge[cte].nxt=head[u],head[u]=cte;}
void aq(int u,int v){ctq++;ques[ctq].to=v,ques[ctq].nxt=hq[u],hq[u]=ctq;}
struct AC{
    int fa[N],son[N][28],ch[N][28],fail[N];
    int cre(int w,int ff){tot++;fa[tot]=ff;return tot;}
    void Build()
    {
        int x=0;
        for(int i=1;i<=len;i++)
        {
            if('a'<=str[i]&&str[i]<='z'){
                int w=idx(str[i]);
                if(!ch[x][w]) ch[x][w]=son[x][w]=cre(w,x);
                x=ch[x][w];
            }else if(str[i]=='B'){
                x=fa[x];
            }else pos[++num]=x;
        }
    }
    void Fail()
    {
        queue<int>q;
        for(int i=1;i<=26;i++)
            if(ch[0][i]) q.push(ch[0][i]),ae(0,ch[0][i]);
        while(!q.empty())
        {
            int x=q.front();q.pop();
            for(int i=1;i<=26;i++)
                if(ch[x][i]){
                    fail[ch[x][i]]=ch[fail[x]][i];
                    ae(fail[ch[x][i]],ch[x][i]);
                    q.push(ch[x][i]);
                }else{
                    ch[x][i]=ch[fail[x]][i];
                }
        }
    }
    void dfs(int u)
    {
        st[u]=++dfn;
        for(int j=head[u];j;j=edge[j].nxt){
            int v=edge[j].to;
            dfs(v);
        }ed[u]=++dfn;
    }
    void main()
    {
        Build();
        Fail();
        dfs(0);
    }
}ac;
struct Ques{int x,y,id,ans;}q[N];
void update(int x,int w){for(int i=x;i<=dfn;i+=lowbit(i))s[i]+=w;}
int query(int x){int ans=0;for(int i=x;i>0;i-=lowbit(i))ans+=s[i];return ans;}
void dfs_ans(int x)
{
    update(st[x],1);
    for(int i=1;i<=26;i++)
    {
        if(ac.son[x][i]) 
            dfs_ans(ac.son[x][i]);
    }
    for(int j=hq[x];j;j=ques[j].nxt){
        int v=ques[j].to;
        q[v].ans=query(ed[pos[q[v].x]])-query(st[pos[q[v].x]]-1);
    }
    update(ed[x],-1);
}

int main()
{
    scanf("%s",str+1);
    len=strlen(str+1);
    ac.main();
    int x,y;
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&x,&y);
        qs++,q[qs].x=x,q[qs].y=y,q[qs].id=i;
        aq(pos[y],qs);
    }
    dfs_ans(0);
    for(int i=1;i<=m;i++){printf("%d\n",q[i].ans);}
    return 0;
}


 

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