• 【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
    ",ans[i]);
        }
        return 0;
    }
  • 相关阅读:
    《C#高级编程(第6版)》第10章筆記第10章集 合
    《C#高级编程(第6版)》第6章筆記第6章运算符和类型强制转换
    解决flash跨域读取XML的问题
    轉:showModalDialog和showModelessDialog使用心得
    用ASP为blog程序编写Trackback功能 小李刀刀(转载)
    轉:VB6中将数据导出到Excel提速之法
    C#讀書資源
    [轉]在SQL Server中使用种子表生成流水号注意顺序
    如何导入导出MySQL数据库*.sql文件操作
    《C#高级编程(第6版)》第7章筆記第7章委托和事件
  • 原文地址:https://www.cnblogs.com/2016gdgzoi471/p/9476875.html
Copyright © 2020-2023  润新知