【bzoj2434】[Noi2011]阿狸的打字机【AC自动机】

题目传送门
题解:由于fail[i]一定是i的一个后缀,所以i这个节点对应的状态一定会在fail树中i的子树的所有状态出现。我们只需要对每个询问x,y求出x对应节点子树内有多少个节点在y对应节点到根的路径上出现过。我们先建出trie树,再把AC自动机搞出来(不要建Trie图!),然后把fail链倒过来建出fail树。接着我们把fail树dfs一遍,得到每个节点的入栈时间l[i]和出栈时间r[i],i对应子树的dfs序区间就是[l[i],r[i]]。我们再dfs一遍原来的trie树,每遇到一个点i就c[l[i]]++,接着对于以i为y串的所有询问的x串对应节点求一下c[l[x]]~c[r[x]]的和,记录一下答案,继续dfs,最后退栈的时候c[l[i]]–。这很显然可以用树状数组解决。于是就搞掂了!
我感觉这次我打的AC自动机好丑啊,而且常数巨大(sb自带大常数)
代码

#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
using namespace std;
const int N=100005;
int n,m,idx,now,cnt=1,dfc,tot,x,y,dy[N],fa[N],fail[N],ch[N][26];
int head[N],to[N],nxt[N],l[N],r[N],c[N],ans[N];
char s[N];
queue<int> q;
vector<int> v[N],v2[N];
void adde(int u,int v){
    to[++tot]=v;
    nxt[tot]=head[u];
    head[u]=tot;
}
void build_ac(){
    q.push(1);
    while(!q.empty()){
        int u=q.front();
        q.pop();
        for(int i=0;i<26;i++){
            if(ch[u][i]){
                int p;
                if(u==1){
                    p=1;
                }else{
                    p=fail[u];
                    while(p!=1&&!ch[p][i]){
                        p=fail[p];
                    }
                    if(!ch[p][i]){
                        p=1;
                    }else{
                        p=ch[p][i];
                    }
                }
                fail[ch[u][i]]=p;
                adde(p,ch[u][i]);
                q.push(ch[u][i]);
            }
        }
    }
}
void dfs1(int u){
    l[u]=++dfc;
    for(int i=head[u];i;i=nxt[i]){
        dfs1(to[i]);
    }
    r[u]=dfc;
}
void add(int i,int v){
    while(i<=dfc){
        c[i]+=v;
        i+=i&(-i);
    }
}
int sum(int i){
    int res=0;
    while(i){
        res+=c[i];
        i-=i&(-i);
    }
    return res;
}
void dfs2(int u){
    add(l[u],1);
    for(int i=0;i<v[u].size();i++){
        ans[v2[u][i]]=sum(r[v[u][i]])-sum(l[v[u][i]]-1);
    }
    for(int i=0;i<26;i++){
        if(ch[u][i]){
            dfs2(ch[u][i]);
        }
    }
    add(l[u],-1);
}
int main(){
    scanf("%s%d",s+1,&m);
    n=strlen(s+1);
    now=1;
    for(int i=1;i<=n;i++){
        if(s[i]>='a'&&s[i]<='z'){
            if(!ch[now][s[i]-'a']){
                ch[now][s[i]-'a']=++cnt;
                fa[cnt]=now;
            }
            now=ch[now][s[i]-'a'];
        }else if(s[i]=='P'){
            dy[++idx]=now;
        }else{
            now=fa[now];
        }
    }
    build_ac();
    dfs1(1);
    for(int i=1;i<=m;i++){
        scanf("%d%d",&x,&y);
        v[dy[y]].push_back(dy[x]);
        v2[dy[y]].push_back(i);
    }
    dfs2(1);
    for(int i=1;i<=m;i++){
        printf("%d\n",ans[i]);
    }
    return 0;
}
posted @ 2018-05-05 21:31  一剑霜寒十四洲  阅读(123)  评论(0编辑  收藏  举报