zoukankan      html  css  js  c++  java
  • AC自动机小结

      

        AC自动机在trie树上实现KMP的一种数据结构,可以完成多模式串的匹配,核心要理解fail指针的含义,即让当前字符失配时跳转到具有最长公共前后缀的字符继续匹配,从根节点到当前节点(s)fail指针的节点(p)的路径字符串必定为从根节点到节点s的路径字符串的一个后缀,还有理解trie图,当字符串在trie树上行走没有路可走时,fail指针指向的节点可相当于字符串要走的下一点,然后再无限匹配下去,具体介绍看trie图的构建、活用与理解

    学习资料:http://blog.csdn.net/niushuai666/article/details/7002823

                  http://hi.baidu.com/nialv7/item/ce1ce015d44a6ba7feded52d

    模板题

    1.HDU 2222

    题意:统计目标串中模式串的个数。

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 500010;
    struct Trie
    {
        int ch[N][26],fail[N],last[N];
        int root,sz;
        int newnode()
        {
            for(int i=0;i<26;i++)ch[sz][i]=-1;
            last[sz++]=0;
            return sz-1;
        }
        void init()
        {
            sz=0;
            root=newnode();
        }
        void insert(char s[])
        {
            int len=strlen(s);
            int now=root;
            for(int i=0;i<len;i++)
            {
                int id=s[i]-'a';
                if(ch[now][id]==-1)
                    ch[now][id]=newnode();
                now=ch[now][id];
            }
            last[now]++;
        }
        void build()
        {
            queue<int>que;
            fail[root]=root;
            for(int i=0;i<26;i++)
            {
                if(ch[root][i]==-1)ch[root][i]=root;
                else
                {
                    fail[ch[root][i]]=root;
                    que.push(ch[root][i]);
                }
            }
            while(!que.empty())
            {
                int now=que.front();que.pop();
                for(int i=0;i<26;i++)
                {
                    if(ch[now][i]==-1)ch[now][i]=ch[fail[now]][i];
                    else
                    {
                        fail[ch[now][i]]=ch[fail[now]][i];
                        que.push(ch[now][i]);
                    }
                }
            }
        }
        int query(char s[])
        {
            int len=strlen(s);
            int now=root,res=0;
            for(int i=0;i<len;i++)
            {
                int id=s[i]-'a';
                now=ch[now][id];
                int temp=now;
                while(temp!=root)
                {
                    res+=last[temp];
                    last[temp]=0;
                    temp=fail[temp];
                }
            }
            return res;
        }
    }ac;
    char str[1000010];
    int main()
    {
        int T,n;
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d",&n);
            ac.init();
            for(int i=0;i<n;i++)
            {
                scanf("%s",str);
                ac.insert(str);
            }
            ac.build();
            scanf("%s",str);
            printf("%d
    ",ac.query(str));
        }
    }
    View Code

    2.HDU 2896

    题意:统计每个目标串中模式串的个数和id。

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 100010;
    struct Trie
    {
        int ch[N][128],fail[N],last[N];
        int root,sz;
        int newnode()
        {
            for(int i=0;i<128;i++)ch[sz][i]=-1;
            last[sz++]=0;
            return sz-1;
        }
        void init()
        {
            sz=0;
            root=newnode();
        }
        void insert(char s[],int num)
        {
            int len=strlen(s);
            int now=root;
            for(int i=0;i<len;i++)
            {
                int id=s[i];
                if(ch[now][id]==-1)
                    ch[now][id]=newnode();
                now=ch[now][id];
            }
            last[now]=num;
        }
        void build()
        {
            queue<int>que;
            fail[root]=root;
            for(int i=0;i<128;i++)
            {
                if(ch[root][i]==-1)ch[root][i]=root;
                else
                {
                    fail[ch[root][i]]=root;
                    que.push(ch[root][i]);
                }
            }
            while(!que.empty())
            {
                int now=que.front();que.pop();
                for(int i=0;i<128;i++)
                {
                    if(ch[now][i]==-1)ch[now][i]=ch[fail[now]][i];
                    else
                    {
                        fail[ch[now][i]]=ch[fail[now]][i];
                        que.push(ch[now][i]);
                    }
                }
            }
        }
        bool vis[510];
        bool query(char s[],int n,int num)
        {
            int len=strlen(s);
            int now=root;
            bool flag=false;
            memset(vis,false,sizeof(vis));
            for(int i=0;i<len;i++)
            {
                int id=s[i];
                now=ch[now][id];
                int temp=now;
                while(temp!=root)
                {
                    if(last[temp])
                    {
                        vis[last[temp]]=true;
                        flag=true;
                    }
                    temp=fail[temp];
                }
            }
            if(!flag)return false;
            printf("web %d:",num);
            for(int i=1;i<=n;i++)
                if(vis[i])printf(" %d",i);
            puts("");
            return true;
        }
    }ac;
    char str[10010];
    int main()
    {
        int n,m;
        while(scanf("%d",&n)>0)
        {
            ac.init();
            for(int i=1;i<=n;i++)
            {
                scanf("%s",str);
                ac.insert(str,i);
            }
            ac.build();
            int ans=0;
            scanf("%d",&m);
            for(int i=1;i<=m;i++)
            {
                scanf("%s",str);
                if(ac.query(str,n,i))ans++;
            }
            printf("total: %d
    ",ans);
        }
    }
    View Code

    3.HDU 3065

    题意:统计每个模式串在目标串中出现的次数。

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 50010;
    char str[1010][110];
    struct Trie
    {
        int ch[N][128],fail[N],last[N];
        int root,sz;
        int newnode()
        {
            for(int i=0;i<128;i++)ch[sz][i]=-1;
            last[sz++]=0;
            return sz-1;
        }
        void init()
        {
            sz=0;
            root=newnode();
        }
        void insert(char s[],int num)
        {
            int len=strlen(s);
            int now=root;
            for(int i=0;i<len;i++)
            {
                int id=s[i];
                if(ch[now][id]==-1)
                    ch[now][id]=newnode();
                now=ch[now][id];
            }
            last[now]=num;
        }
        void build()
        {
            queue<int>que;
            fail[root]=root;
            for(int i=0;i<128;i++)
            {
                if(ch[root][i]==-1)ch[root][i]=root;
                else
                {
                    fail[ch[root][i]]=root;
                    que.push(ch[root][i]);
                }
            }
            while(!que.empty())
            {
                int now=que.front();que.pop();
                for(int i=0;i<128;i++)
                {
                    if(ch[now][i]==-1)ch[now][i]=ch[fail[now]][i];
                    else
                    {
                        fail[ch[now][i]]=ch[fail[now]][i];
                        que.push(ch[now][i]);
                    }
                }
            }
        }
        int vis[1010];
        void query(char s[],int n)
        {
            int len=strlen(s);
            int now=root;
            memset(vis,0,sizeof(vis));
            for(int i=0;i<len;i++)
            {
                int id=s[i];
                now=ch[now][id];
                int temp=now;
                while(temp!=root)
                {
                    if(last[temp])
                    {
                        vis[last[temp]]++;
                    }
                    temp=fail[temp];
                }
            }
            for(int i=1;i<=n;i++)
                if(vis[i])printf("%s: %d
    ",str[i],vis[i]);
        }
    }ac;
    char s[2000010];
    int main()
    {
        int n,m;
        while(scanf("%d",&n)>0)
        {
            ac.init();
            for(int i=1;i<=n;i++)
            {
                scanf("%s",str[i]);
                ac.insert(str[i],i);
            }
            ac.build();
            scanf("%s",s);
            ac.query(s,n);
        }
    }
    View Code

    4.ZOJ 3228

    题意:统计模式串在目标串中出现的次数,一种是可重叠的,一种是不可重叠的。

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 600010;
    struct Trie
    {
        int ch[N][26],fail[N],len[N];
        int root,sz;
        int newnode()
        {
            for(int i=0;i<26;i++)ch[sz][i]=-1;
            last[sz++]=0;
            return sz-1;
        }
        void init()
        {
            sz=0;
            root=newnode();
        }
        int insert(char s[])
        {
            int lens=strlen(s);
            int now=root;
            for(int i=0;i<lens;i++)
            {
                int id=s[i]-'a';
                if(ch[now][id]==-1)
                {
                    ch[now][id]=newnode();
                    len[ch[now][id]]=i+1;
                }
                now=ch[now][id];
            }
            return now;
        }
        void build()
        {
            queue<int>que;
            fail[root]=root;
            for(int i=0;i<26;i++)
            {
                if(ch[root][i]==-1)ch[root][i]=root;
                else
                {
                    fail[ch[root][i]]=root;
                    que.push(ch[root][i]);
                }
            }
            while(!que.empty())
            {
                int now=que.front();que.pop();
                for(int i=0;i<26;i++)
                {
                    if(ch[now][i]==-1)ch[now][i]=ch[fail[now]][i];
                    else
                    {
                        fail[ch[now][i]]=ch[fail[now]][i];
                        que.push(ch[now][i]);
                    }
                }
            }
        }
        int cnt[N][2],last[N];
        void query(char s[])
        {
            int lens=strlen(s);
            int now=root;
            memset(cnt,0,sizeof(cnt));
            memset(last,0,sizeof(last));
            for(int i=0;i<lens;i++)
            {
                int id=s[i]-'a';
                now=ch[now][id];
                int temp=now;
                while(temp!=root)
                {
                    cnt[temp][0]++;
                    if(i+1-last[temp]>=len[temp])
                    {
                        last[temp]=i+1;
                        cnt[temp][1]++;
                    }
                    temp=fail[temp];
                }
            }
        }
    }ac;
    char s[N],str[20];
    int pos[N],type[N];
    int main()
    {
        int n,cas=1;
        while(scanf("%s",s)>0)
        {
            ac.init();
            scanf("%d",&n);
            for(int i=0;i<n;i++)
            {
                scanf("%d%s",&type[i],str);
                pos[i]=ac.insert(str);
            }
            ac.build();
            ac.query(s);
            printf("Case %d
    ",cas++);
            for(int i=0;i<n;i++)printf("%d
    ",ac.cnt[pos[i]][type[i]]);
            puts("");
        }
    }
    View Code

    矩阵

    1.POJ 2778

    题意:有m种DNA序列是有病毒的,问有多少种长度为n的DNA序列不包含任何一种有病毒的DNA序列。

    分析:在trie图上走n步不包含病毒的节点,建立好矩阵进行状态转移,和有向图中走n步从一点到另一点一样,直接矩阵快速幂。

    #include <cstdio>
    #include <cstring>
    #include <queue>
    #include <algorithm>
    using namespace std;
    const int N = 110;
    const int mod = 100000;
    struct matrix
    {
        int m[N][N],n;
        matrix(){}
        matrix(int _n)
        {
            n=_n;
            for(int i=0;i<n;i++)
                for(int j=0;j<n;j++)
                m[i][j]=0;
        }
        matrix operator*(const matrix &a)const
        {
            matrix res=matrix(n);
            for(int k=0;k<n;k++)
            for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
            {
                res.m[i][j]=(res.m[i][j]+1ll*m[i][k]*a.m[k][j])%mod;
            }
            return res;
        }
    };
    struct Trie
    {
        int ch[N][4],fail[N];
        bool last[N];
        int root,sz;
        int newnode()
        {
            for(int i=0;i<4;i++)ch[sz][i]=-1;
            last[sz++]=false;
            return sz-1;
        }
        void init()
        {
            sz=0;
            root=newnode();
        }
        int getid(char ch)
        {
            if(ch=='A')return 0;
            else if(ch=='C')return 1;
            else if(ch=='G')return 2;
            else return 3;
        }
        void insert(char s[])
        {
            int lens=strlen(s);
            int now=root;
            for(int i=0;i<lens;i++)
            {
                int id=getid(s[i]);
                if(ch[now][id]==-1)
                {
                    ch[now][id]=newnode();
                }
                now=ch[now][id];
            }
            last[now]=true;
        }
        void build()
        {
            queue<int>que;
            fail[root]=root;
            for(int i=0;i<4;i++)
            {
                if(ch[root][i]==-1)ch[root][i]=root;
                else
                {
                    fail[ch[root][i]]=root;
                    que.push(ch[root][i]);
                }
            }
            while(!que.empty())
            {
                int now=que.front();que.pop();
                if(last[fail[now]])last[now]=true;
                for(int i=0;i<4;i++)
                {
                    if(ch[now][i]==-1)ch[now][i]=ch[fail[now]][i];
                    else
                    {
                        fail[ch[now][i]]=ch[fail[now]][i];
                        que.push(ch[now][i]);
                    }
                }
            }
        }
        matrix getmat()
        {
            matrix res=matrix(sz);
            for(int i=0;i<sz;i++)
                for(int j=0;j<4;j++)
                if(!last[ch[i][j]])res.m[i][ch[i][j]]++;
            return res;
        }
    }ac;
    matrix quick_mod(matrix a,int n)
    {
        matrix res=matrix(ac.sz);
        for(int i=0;i<ac.sz;i++)res.m[i][i]=1;
        while(n)
        {
            if(n&1)res=res*a;
            a=a*a;
            n>>=1;
        }
        return res;
    }
    int main()
    {
        int n,m;
        while(scanf("%d%d",&n,&m)>0)
        {
            ac.init();
            for(int i=1;i<=n;i++)
            {
                char s[15];
                scanf("%s",s);
                ac.insert(s);
            }
            ac.build();
            matrix a=ac.getmat();
            a=quick_mod(a,m);
            int ans=0;
            for(int i=0;i<ac.sz;i++)
            {
                ans+=a.m[0][i];
            }
            ans%=mod;
            printf("%d
    ",ans);
        }
    }
    View Code

    2.HDU 2243

    题意:给你n 个单词,求出满足以下条件的单词个数:长度不大于L 且单词中至少包含一个子串为前面n 个单词中任意一个。

    分析:先求出所有单词的数量 26^1+ 26^2+ ... + 26^L,可以构造矩阵乘法求出。然后求出所有不包含 n 个单词的串的数量,两者相减就是答案了。

    #include <cstdio>
    #include <cstring>
    #include <queue>
    #include <algorithm>
    using namespace std;
    typedef unsigned long long ull;
    const int N = 50;
    const int mod = 100000;
    struct matrix
    {
        ull m[N*2][N*2],n;
        matrix(){}
        matrix(int _n)
        {
            n=_n;
            for(int i=0;i<n;i++)
                for(int j=0;j<n;j++)
                m[i][j]=0;
        }
        matrix operator*(const matrix &a)const
        {
            matrix res=matrix(n);
            for(int k=0;k<n;k++)
            for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
            {
                res.m[i][j]=res.m[i][j]+m[i][k]*a.m[k][j];
            }
            return res;
        }
    };
    struct Trie
    {
        int ch[N][26],fail[N];
        bool last[N];
        int root,sz;
        int newnode()
        {
            for(int i=0;i<26;i++)ch[sz][i]=-1;
            last[sz++]=false;
            return sz-1;
        }
        void init()
        {
            sz=0;
            root=newnode();
        }
        void insert(char s[])
        {
            int lens=strlen(s);
            int now=root;
            for(int i=0;i<lens;i++)
            {
                int id=s[i]-'a';
                if(ch[now][id]==-1)
                {
                    ch[now][id]=newnode();
                }
                now=ch[now][id];
            }
            last[now]=true;
        }
        void build()
        {
            queue<int>que;
            fail[root]=root;
            for(int i=0;i<26;i++)
            {
                if(ch[root][i]==-1)ch[root][i]=root;
                else
                {
                    fail[ch[root][i]]=root;
                    que.push(ch[root][i]);
                }
            }
            while(!que.empty())
            {
                int now=que.front();que.pop();
                if(last[fail[now]])last[now]=true;
                for(int i=0;i<26;i++)
                {
                    if(ch[now][i]==-1)ch[now][i]=ch[fail[now]][i];
                    else
                    {
                        fail[ch[now][i]]=ch[fail[now]][i];
                        que.push(ch[now][i]);
                    }
                }
            }
        }
        matrix getmat()
        {
            matrix res=matrix(sz*2);
            for(int i=0;i<sz;i++)
                for(int j=0;j<26;j++)
                if(!last[ch[i][j]])res.m[i][ch[i][j]]++;
            for(int i=0;i<sz;i++)res.m[i][i+sz]=res.m[i+sz][i+sz]=1;
            return res;
        }
    }ac;
    matrix quick_mod(matrix a,int n)
    {
        matrix res=matrix(a.n);
        for(int i=0;i<a.n;i++)res.m[i][i]=1;
        while(n)
        {
            if(n&1)res=res*a;
            a=a*a;
            n>>=1;
        }
        return res;
    }
    int main()
    {
        int n,m;
        char s[15];
        while(scanf("%d%d",&n,&m)>0)
        {
            ac.init();
            for(int i=1;i<=n;i++)
            {
                scanf("%s",s);
                ac.insert(s);
            }
            ac.build();
            matrix a=ac.getmat();
            a=quick_mod(a,m);
            ull ans1=0;
            for(int i=0;i<ac.sz*2;i++)
            {
                ans1+=a.m[0][i];
            }
            ans1--;
            matrix b=matrix(2);
            b.m[0][0]=26;b.m[0][1]=1;
            b.m[1][0]=0;b.m[1][1]=1;
            b=quick_mod(b,m);
            ull ans2=0;
            ans2=b.m[0][0]+b.m[0][1];
            ans2--;
            printf("%I64u
    ",ans2-ans1);
        }
    }
    View Code

    DP

    1.POJ 1625

    题意:给出病毒串,求长度为n且不含病毒串的DNA种数。

    分析:dp[i][j]表示长度为i时,以自动机上结点编号为j结尾的种数。dp[i][j]+=dp[i-1][k],k是自动机上能转移到j的结点,且j、k都不是病毒串的结尾。(dp[0][0]=1)

    #include <cstdio>
    #include <cstring>
    #include <queue>
    #include <algorithm>
    #include <map>
    using namespace std;
    const int N = 110;
    const int mod = 100000;
    map<char,int>mp;
    struct BigInt
    {
        const static int mod = 10000;
        const static int DLEN = 4;
        int a[600],len;
        BigInt()
        {
            memset(a,0,sizeof(a));
            len = 1;
        }
        BigInt(int v)
        {
            memset(a,0,sizeof(a));
            len = 0;
            do
            {
                a[len++] = v%mod;
                v /= mod;
            }while(v);
        }
        BigInt(const char s[])
        {
            memset(a,0,sizeof(a));
            int L = strlen(s);
            len = L/DLEN;
            if(L%DLEN)len++;
            int index = 0;
            for(int i = L-1;i >= 0;i -= DLEN)
            {
                int t = 0;
                int k = i - DLEN + 1;
                if(k < 0)k = 0;
                for(int j = k;j <= i;j++)
                    t = t*10 + s[j] - '0';
                a[index++] = t;
            }
        }
        BigInt operator +(const BigInt &b)const
        {
            BigInt res;
            res.len = max(len,b.len);
            for(int i = 0;i <= res.len;i++)
                res.a[i] = 0;
            for(int i = 0;i < res.len;i++)
            {
                res.a[i] += ((i < len)?a[i]:0)+((i < b.len)?b.a[i]:0);
                res.a[i+1] += res.a[i]/mod;
                res.a[i] %= mod;
            }
            if(res.a[res.len] > 0)res.len++;
            return res;
        }
        BigInt operator *(const BigInt &b)const
        {
            BigInt res;
            for(int i = 0; i < len;i++)
            {
                int up = 0;
                for(int j = 0;j < b.len;j++)
                {
                    int temp = a[i]*b.a[j] + res.a[i+j] + up;
                    res.a[i+j] = temp%mod;
                    up = temp/mod;
                }
                if(up != 0)
                    res.a[i + b.len] = up;
            }
            res.len = len + b.len;
            while(res.a[res.len - 1] == 0 &&res.len > 1)res.len--;
            return res;
        }
        void output()
        {
            printf("%d",a[len-1]);
            for(int i = len-2;i >=0 ;i--)
                printf("%04d",a[i]);
            printf("
    ");
        }
    };
    struct matrix
    {
        int m[N][N];
        int n;
        matrix(){}
        matrix(int _n)
        {
            n=_n;
            for(int i=0;i<n;i++)
                for(int j=0;j<n;j++)
                m[i][j]=0;
        }
    };
    struct Trie
    {
        int ch[N][256],fail[N];
        bool last[N];
        int root,sz;
        int newnode()
        {
            for(int i=0;i<256;i++)ch[sz][i]=-1;
            last[sz++]=false;
            return sz-1;
        }
        void init()
        {
            sz=0;
            root=newnode();
        }
        void insert(char s[])
        {
            int lens=strlen(s);
            int now=root;
            for(int i=0;i<lens;i++)
            {
                int id=mp[s[i]];
                if(ch[now][id]==-1)
                {
                    ch[now][id]=newnode();
                }
                now=ch[now][id];
            }
            last[now]=true;
        }
        void build()
        {
            queue<int>que;
            fail[root]=root;
            for(int i=0;i<256;i++)
            {
                if(ch[root][i]==-1)ch[root][i]=root;
                else
                {
                    fail[ch[root][i]]=root;
                    que.push(ch[root][i]);
                }
            }
            while(!que.empty())
            {
                int now=que.front();que.pop();
                if(last[fail[now]])last[now]=true;
                for(int i=0;i<256;i++)
                {
                    if(ch[now][i]==-1)ch[now][i]=ch[fail[now]][i];
                    else
                    {
                        fail[ch[now][i]]=ch[fail[now]][i];
                        que.push(ch[now][i]);
                    }
                }
            }
        }
        matrix getmat(int n)
        {
            matrix res=matrix(sz);
            for(int i=0;i<sz;i++)
                for(int j=0;j<n;j++)
                if(!last[ch[i][j]])res.m[i][ch[i][j]]++;
            return res;
        }
    }ac;
    
    char buf[1010];
    BigInt dp[2][110];
    int main()
    {
        int n,m,p;
        while(scanf("%d%d%d",&n,&m,&p)>0)
        {
            getchar();
            gets(buf);
            mp.clear();
            int len = strlen(buf);
            for(int i = 0;i < len;i++)
                mp[buf[i]]=i;
            ac.init();
            for(int i = 0;i < p;i++)
            {
                gets(buf);
                ac.insert(buf);
            }
            ac.build();
            matrix a= ac.getmat(n);
            int now=0;
            dp[now][0]=1;
            for(int i=1;i<a.n;i++)dp[now][i]=0;
            for(int i=1;i<=m;i++)
            {
                now^=1;
                for(int j=0;j<a.n;j++)dp[now][j]=0;
                for(int j=0;j<a.n;j++)
                    for(int k=0;k<a.n;k++)
                    if(a.m[j][k])dp[now][k]=dp[now][k]+dp[now^1][j]*a.m[j][k];
            }
            BigInt ans = 0;
            for(int i = 0;i < a.n;i++)
                ans = ans + dp[now][i];
            ans.output();
        }
        return 0;
    }
    View Code

    2.HDU 2825

    题意:给出字符串(<10),求至少由x个字符串组成的长度为n的字符串种数。

    分析:dp[i][j][k]表示长度为i时,以自动机上结点编号为j结尾,把用到的串二进制压缩为k的种数。dp[i][j][k]+=dp[i-1][p][t],p是自动机上能转移到j的结点。(dp[0][0][0]=1)

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <queue>
    #include <vector>
    using namespace std;
    const int mod = 20090717;
    const int maxn = 103;
    const int max_num = 26;
    int idx[256];
    int n, m, x;
    int dp[26][103][1026];
    bool vis[26][103][1026];
    int cnt[1025];
    struct node {
        int v, p, zt;
        node(){}
        node(int v, int p, int zt) : v(v), p(p), zt(zt){}
    }Q[1000006];
    struct AcAuto {
        int val[maxn], f[maxn];
        int ch[maxn][max_num], tot;
    
        void init() {
            tot = 0;
            new_node();
            int i;
            for(i = 0; i < 26; i++)
                idx['a'+i] = i;
        }
        inline int new_node() {
            memset(ch[tot], 0, sizeof(ch[tot]));
            val[tot] = 0;
            f[tot] = 0;
            return tot++;
        }
    
        void insert(char *s, int id) {
            int i, j, p = 0;
            for(;*s; s++) {
                int k = idx[*s];
                if(!ch[p][k]) ch[p][k] = new_node();
                p = ch[p][k];
            }
            val[p] |= 1<<id;
        }
        void getfail() {
            int i, j, p = 0;
            int q[maxn];
            int *s = q, *e = q;
            for(i = 0; i < max_num; i++) if(ch[0][i]) *e++ = ch[0][i];
            while(s != e) {
                int u = *s++;
                for(i = 0; i < max_num; i++) {
                    int &v = ch[u][i];
                    if(!v) { v = ch[f[u]][i]; continue; }
                    *e++ = v;
                    j = f[u];
                    while(j && !ch[j][i]) j = f[j];
                    f[v] = ch[j][i];
                    val[v] |= val[f[v]];
                }
            }
        }
        void solve() {
            int i, j, k, u;
            int M = (1<<m);
            for(i = 0; i <= n; i++)
                for(k = 0; k < tot; k++)
                    for(j = 0; j < M; j++)
                        dp[i][k][j] = 0;
            dp[0][0][0] = 1;
    
            node *s = Q, *e = Q;
            *e++ = node(0, 0, 0);
            vis[0][0][0] = 1;
            while(s != e) {
                node u = *s++;
                vis[u.v][u.p][u.zt] = 0;
                if(u.v >= n) continue;
    
                for(i = 0; i < max_num; i++) {
                    int p = ch[u.p][i];
                    node v = node(u.v+1, p, u.zt|val[p]);
    
                    dp[v.v][v.p][v.zt] += dp[u.v][u.p][u.zt];
                    if(dp[v.v][v.p][v.zt] >= mod) dp[v.v][v.p][v.zt] -= mod;
    
                    if(!vis[v.v][v.p][v.zt]) {
                        vis[v.v][v.p][v.zt] = 1;
                        *e++ = v;
                    }
                }
            }
    
            int ans = 0;
            for(i = 0; i < M; i++) {
                if(cnt[i] >= x)
                for(j = 0; j < tot; j++) {
                    ans += dp[n][j][i];
                    if(ans >= mod) ans -= mod;
                }
            }
            printf("%d
    ", ans);
        }
    }AC;
    
    char str[13];
    int main() {
        int i, j;
        for(i = 0; i < 1024; i++) {
            int c = 0;
            for(j = i; j; j -= (j&-j)) c++;
            cnt[i] = c;
        }
        while( ~scanf("%d%d%d", &n, &m, &x) && (n || m || x)) {
            AC.init();
            for(i = 0; i < m; i++) {
                scanf("%s", str);
                AC.insert(str, i);
            }
            AC.getfail();
            AC.solve();
        }
        return 0;
    }
    View Code

    3.UVALive 6806

    题意:给定n个字符的花费代价,m个字符串的获取价值及现有的总费用B,求在费用B范围内构造一个拥有最大价值的字符串(包含几个价值字符串获得多大价值)。

    分析:构建trie图获取fail指针时顺便总计到达每个节点得到的总价值,及每个节点都加上它fail指针指向的节点价值,然后进行dp。

    dp[i][j]表示到达第i节点时花费j得到最大总价值。

    #include <cstdio>
    #include <cstring>
    #include <queue>
    #include <algorithm>
    #include <map>
    using namespace std;
    const int N = 10010;
    struct Trie
    {
        int ch[N][26],fail[N],val[N];
        int root,sz;
        int newnode()
        {
            for(int i=0; i<26; i++)ch[sz][i]=-1;
            val[sz++]=0;
            return sz-1;
        }
        void init()
        {
            sz=0;
            root=newnode();
        }
        void insert(char s[],int c)
        {
            int len=strlen(s);
            int now=root;
            for(int i=0; i<len; i++)
            {
                int id=s[i]-'A';
                if(ch[now][id]==-1)ch[now][id]=newnode();
                now=ch[now][id];
            }
            val[now]+=c;
        }
        void build()
        {
            queue<int>que;
            fail[root]=root;
            for(int i=0; i<26; i++)
            {
                if(ch[root][i]==-1)ch[root][i]=root;
                else
                {
                    fail[ch[root][i]]=root;
                    que.push(ch[root][i]);
                }
            }
            while(!que.empty())
            {
                int now=que.front();
                que.pop();
                val[now]+=val[fail[now]];
                for(int i=0; i<26; i++)
                {
                    if(ch[now][i]==-1)ch[now][i]=ch[fail[now]][i];
                    else
                    {
                        fail[ch[now][i]]=ch[fail[now]][i];
                        que.push(ch[now][i]);
                    }
                }
            }
        }
    } ac;
    char s[110];
    int c[30],dp[N][210];
    int main()
    {
        int T,n,m,b,x,cas=1;
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d%d%d",&n,&m,&b);
            memset(c,0,sizeof(c));
            for(int i=0; i<n; i++)
            {
                scanf("%s%d",s,&x);
                c[s[0]-'A']=x;
            }
            ac.init();
            for(int i=0; i<m; i++)
            {
                scanf("%s%d",s,&x);
                ac.insert(s,x);
            }
            ac.build();
            memset(dp,-1,sizeof(dp));
            dp[0][0]=0;
            int ans=0;
            for(int k=0; k<b; k++)
            {
                for(int i=0; i<ac.sz; i++)
                {
                    if(dp[i][k]==-1)continue;
                    for(int j=0; j<26; j++)
                    {
                        if(!c[j]||k+c[j]>b)continue;
                        int now=ac.ch[i][j],w=c[j];
                        dp[now][k+w]=max(dp[now][k+w],dp[i][k]+ac.val[now]);
                        ans=max(ans,dp[now][k+w]);
                    }
                }
            }
            printf("Case #%d: %d
    ",cas++,ans);
        }
    }
    View Code

    4.HDU 2457

    题意:给出病毒串(<50),求最少的修改次数,使得DNA串不含病毒串。

    分析:dp[i][j]表示长度为i时,以自动机上结点编号为j结尾,使得该DNA串不含病毒串的最小修改次数。每次转移枚举是否需要修改,以及需要修改的字符。

    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <queue>
    #include <algorithm>
    using namespace std;
    const int N = 1050;
    const int inf = 0x3f3f3f3f;
    int dp[N][N];
    int n;
    struct Trie
    {
        int ch[N][4],fail[N];
        bool last[N];
        int root,sz;
        int newnode()
        {
            for(int i=0;i<4;i++)ch[sz][i]=-1;
            last[sz++]=false;
            return sz-1;
        }
        void init()
        {
            sz=0;
            root=newnode();
        }
        int getid(char ch)
        {
            if(ch=='A')return 0;
            else if(ch=='C')return 1;
            else if(ch=='G')return 2;
            else return 3;
        }
        void insert(char s[])
        {
            int len=strlen(s);
            int now=root;
            for(int i=0;i<len;i++)
            {
                int id=getid(s[i]);
                if(ch[now][id]==-1)ch[now][id]=newnode();
                now=ch[now][id];
            }
            last[now]=true;
        }
        void build()
        {
            queue<int>que;
            fail[root]=root;
            for(int i=0;i<4;i++)
            {
                if(ch[root][i]==-1)ch[root][i]=root;
                else
                {
                    fail[ch[root][i]]=root;
                    que.push(ch[root][i]);
                }
            }
            while(!que.empty())
            {
                int now=que.front();que.pop();
                if(last[fail[now]])last[now]=true;
                for(int i=0;i<4;i++)
                {
                    if(ch[now][i]==-1)ch[now][i]=ch[fail[now]][i];
                    else
                    {
                        fail[ch[now][i]]=ch[fail[now]][i];
                        que.push(ch[now][i]);
                    }
                }
            }
        }
        int solve(char s[])
        {
            int len=strlen(s);
            for(int i=0;i<=len;i++)
                for(int j=0;j<sz;j++)dp[i][j]=inf;
            dp[0][0]=0;
            for(int i=0;i<len;i++)
            {
                int t=getid(s[i]);
                for(int j=0;j<sz;j++)
                {
                    if(dp[i][j]>=inf)continue;
                    for(int k=0;k<4;k++)
                    {
                        int now=ch[j][k];
                        if(last[now])continue;
                        dp[i+1][now]=min(dp[i+1][now],dp[i][j]+(t!=k));
                    }
                }
            }
            int ans=inf;
            for(int i=0;i<sz;i++)
                ans=min(ans,dp[len][i]);
            return ans==inf?-1:ans;
        }
    }ac;
    char s[N];
    int main()
    {
        int cas=1;
       while(scanf("%d",&n),n)
       {
           ac.init();
           for(int i=0;i<n;i++)
           {
               scanf("%s",s);
               ac.insert(s);
           }
           ac.build();
           scanf("%s",s);
           printf("Case %d: %d
    ",cas++,ac.solve(s));
       }
    }
    View Code

    4.HDU 3247

    题意:有n(<10)个01串以及m(<1000)个病毒串,将01串拼接成最短的且不含病毒串。

    分析:将01串和病毒串一起构造自动机,枚举每个01串的结尾广搜,可以得到该串和其他01串结尾的最短距离。

    对n个01串二进制压缩,即TSP,dp[i][j]表示状态为i,最后拼接上的01串是第j个。这题要先排除各个串相互

    为子串的情况,否则得到的必定不是最优答案。这题的数据太弱了,网上很多代码都过不了这几组数据也能AC.

    Input:
    3 1
    00000
    00000
    11111
    01
    3 2
    101
    010
    1111
    001
    011
    2 2
    1110
    0111
    101
    1001
    3 3
    0001
    0000
    10000
    010
    101
    111
    3 3
    00000
    00000
    00000
    101
    101
    101

    OutPut:
    10
    7
    5
    6
    5

    #include <cstdio>
    #include <cstring>
    #include <string>
    #include <iostream>
    #include <queue>
    #include <algorithm>
    using namespace std;
    const int N = 200010;
    const int inf = 0x3f3f3f3f;
    int dp[1<<15][15];
    string s[15];
    struct Trie
    {
        int ch[N][2],fail[N],last[N],len[N],d[N],pos[15],dis[15][15];
        int root,sz,cnt;
        int newnode()
        {
            for(int i=0;i<2;i++)ch[sz][i]=-1;
            last[sz++]=0;
            return sz-1;
        }
        void init()
        {
            sz=0;cnt=0;
            root=newnode();
        }
        void insert(string str,int id)
        {
            int len=str.size();
            int now=root;
            for(int i=0;i<len;i++)
            {
                int id=str[i]-'0';
                if(ch[now][id]==-1)ch[now][id]=newnode();
                now=ch[now][id];
            }
            last[now]=id;
        }
        void build()
        {
            queue<int>que;
            fail[root]=root;
            for(int i=0;i<2;i++)
            {
                if(ch[root][i]==-1)ch[root][i]=root;
                else
                {
                    fail[ch[root][i]]=root;
                    que.push(ch[root][i]);
                }
            }
            while(!que.empty())
            {
                int now=que.front();que.pop();
                if(last[fail[now]]==-1)last[now]=-1;
                for(int i=0;i<2;i++)
                {
                    if(ch[now][i]==-1)ch[now][i]=ch[fail[now]][i];
                    else
                    {
                        fail[ch[now][i]]=ch[fail[now]][i];
                        que.push(ch[now][i]);
                    }
                }
            }
        }
        bool vis[N];
        void bfs(int u,int k)
        {
            memset(vis,false,sizeof(vis));
            memset(d,-1,sizeof(d));
            queue<int>que;
            d[u]=0;vis[u]=true;
            que.push(u);
            while(!que.empty())
            {
                int now=que.front();que.pop();
                for(int i=0;i<2;i++)
                {
                    int nxt=ch[now][i];
                    if(last[nxt]!=-1&&!vis[nxt])
                    {
                        vis[nxt]=true;
                        d[nxt]=d[now]+1;
                        que.push(nxt);
                    }
                }
            }
            for(int i=0;i<cnt;i++)dis[k][i]=d[pos[i]];
        }
        int solve(int n)
        {
            for(int i=0;i<(1<<n);i++)
                for(int j=0;j<n;j++)
                    dp[i][j]=inf;
            for(int i=0;i<sz;i++)
                if(last[i]>0)pos[cnt]=i,len[cnt++]=s[last[i]].size();
            for(int i=0;i<cnt;i++)bfs(pos[i],i);
            int ans=inf;
            for(int s=0;s<(1<<cnt);s++)
            {
                for(int i=0;i<cnt;i++)
                {
                    if(s&(1<<i))
                    {
                        if(s==1<<i)dp[s][i]=len[i];
                        for(int j=0;j<cnt;j++)
                        {
                            if(i==j||(s&(1<<j))||dis[i][j]==-1)continue;
                            dp[s|(1<<j)][j]=min(dp[s|(1<<j)][j],dp[s][i]+dis[i][j]);
                        }
                    }
                }
            }
            for(int i=0;i<cnt;i++)ans=min(dp[(1<<cnt)-1][i],ans);
            return ans;
        }
    }ac;
    int n,m;
    int cmp(string s1,string s2)
    {
        return s1.size()>s2.size();
    }
    void solve_sub()
    {
        int tmp=n,k,vis[15];
        sort(s+1,s+n+1,cmp);
        memset(vis,0,sizeof(vis));
        for(int i=1;i<=n;i++)
            for(int j=i+1;j<=n;j++)
            if(s[i].find(s[j])!=-1)vis[j]=1;
        for(n=0,k=1;k<=tmp;k++)
        {
            if(!vis[k])s[++n]=s[k];
        }
    }
    int main()
    {
    
        while(scanf("%d%d",&n,&m)>0)
        {
            if(n+m==0)break;
            ac.init();
            for(int i=1;i<=n;i++)
            {
                cin>>s[i];
            }
            solve_sub();
            for(int i=1;i<=n;i++)ac.insert(s[i],i);
            for(int i=0;i<m;i++)
            {
                cin>>s[0];
                ac.insert(s[0],-1);
            }
            ac.build();
            printf("%d
    ",ac.solve(n));
        }
    }
    View Code
  • 相关阅读:
    Unique constraint on single String column with GreenDao2
    Unique constraint on single String column with GreenDao
    将String转换成InputStream
    TypeError: unsupported operand type(s) for +: 'float' and 'str'
    Could not find private key file: AuthKey_NCD8233CS5.p8
    【Winows10】添加桌面小工具(在桌面显示时钟,日历)
    【Windows10】禁用开机启动项
    SQL如何查询出某一列中不同值出现的次数?
    使用 管理项目依赖
    Warning: Python 3.6 was not found on your system…
  • 原文地址:https://www.cnblogs.com/lienus/p/4451966.html
Copyright © 2011-2022 走看看