zoukankan      html  css  js  c++  java
  • ac自动机

    https://www.cnblogs.com/sclbgw7/p/9875671.html

    讲的好的博客。

    只有fail指针的模板,好像用不到,一般都是用last指针的模板。

    void build()
    {
        queue<int>q;
        q.push(1);
        while(!q.empty())
        {
            int x=q.front();q.pop();
            for(int i=0;i<26;++i)
            {
                int c=ch[x][i];
                if(!c){ch[x][i]=ch[fail[x]][i];continue;}//关键,把子节点改成fail节点的子节点
                q.push(c);
                int fa=fail[x];
                while(fa&&!ch[fa][i])fa=fail[fa];
                fail[c]=ch[fa][i];
            }
        }
    }

    https://cn.vjudge.net/contest/301351#problem/A

    习题

    最基础的模板,即求一个文本串有多少个匹配的模式串。

    #include<queue>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    
    using namespace std;
    
    const int maxn = 5e5 + 5;
    const int N = 1e6 + 5;
    
    int tree[maxn][27];
    int val[maxn];
    int f[maxn];
    int last[maxn];
    int tot = 1;
    
    void inset(char s[]) {
        int root = 0;
        int len = strlen(s);
        for(int i = 0; i < len; i++) {
            int id = s[i] - 'a';
            if(!tree[root][id]) {
                memset(tree[tot], 0, sizeof(tree[tot]));
                val[tot] = 0;
                tree[root][id] = tot++;
            }
            root = tree[root][id];
        }
        val[root]++;
    }
    
    void getfail() {
        queue<int> q;
        f[0] = 0;
        for(int i = 0; i < 26; i++) {
            int u = tree[0][i];
            if(u) {
                f[u] = 0;
                q.push(u);
                last[u] = 0;
            }
        }
        while(!q.empty()) {
            int now = q.front();
            q.pop();
            for(int i = 0; i < 26; i++) {
                int u = tree[now][i];
                if(!u) {
                    tree[now][i] = tree[f[now] ][i];
                    continue;
                }
                q.push(u);
                int v = f[now];
                f[u] = tree[v][i];
                last[u] = val[f[u] ] ? f[u] : last[f[u] ];
            }
        }
    }
    
    int query(char s[]) {
        int now = 0, ans = 0;
        int len = strlen(s);
        for(int i = 0; i < len; i++) {
            now = tree[now][s[i] - 'a' ];
    //        for(int j = now; j && val[j] != -1; j = last[j]) {
    //            ans += val[j];
    //            val[j] = -1;
    //        }
            int tmp = now;
    //如果匹配到根节点就停止,其他情况下如果val[tmp]等于0或1都加上,0加上不影响结果。
    while(tmp) { ans += val[tmp]; val[tmp] = 0; tmp = last[tmp]; } } return ans; } char s[N]; int main() { int n, t; scanf("%d", &t); while(t--) { scanf("%d", &n); tot = 1; memset(tree[0], 0, sizeof(tree[0])); for(int i = 0; i < n; i++) { scanf("%s", s); inset(s); } getfail(); scanf("%s", s); printf("%d ", query(s)); } return 0; }

    DNA Sequence

     POJ - 2778

    https://www.cnblogs.com/LQLlulu/p/9344774.html

    一到ac自动机加上矩阵快速幂的题目

    题意

      题目给出m(m<=10)个仅仅由A,T,C,G组成的单词(单词长度不超过10),然后给出一个整数n(n<=2000000000),问你用这四个字母组成一个长度为n的长文本,有多少种组成方法可以使得它不含任何一个给出的单词。

    分析

      当时一看以为是跟训练指南上(UVA11468)一样的题,感觉只有四个字母并且单词数量和长度也比较小,但是一看给出的n有点懵逼。如果再按照书上建立AC自动机以后直接跑DP的方法肯定是不行了。然后我们就要用到,递推利器,矩阵快速幂。

      我们还是按照套路先把AC自动机建出来,然后将每个单词结点设为非法结点,题目变成在AC自动机中走n步不通过非法结点的方案数。然后设f[i][j]是当前在结点i,已经走了j步,且未走过非法结点的方案数。然后怎么转移呢?

      f[i][j]=A(0,i)*f[0][j-1]+A(1,i)*f[1][j-1]+...+A(sz-1,i)f[sz-1][j-1]。其中A(i,j)的含义就是从i到j有几条直接连接的边。那么将这个dp方程拆开来看

      

      f0[i]=A(0,0)*f[0][i-1]+A(1,0)*f[1][i-1]+....+A(sz-1,0)*fsz-1[i-1]

      f1[i]=A(0,1)*f[0][i-1]+A(1,1)*f[1][i-1]+....+A(sz-1,1)*fsz-1[i-1]

      .

      .

      fsz-1[i]=A(0,sz-1)*f[0][i-1]+A(1,sz-1)*f[1][i-1]+...+A(sz-1,sz-1)*fsz-1[i-1]

     然后根据这个我们就很好建立转移矩阵

     *

    我的理解:每一项表示i到j的直接路径条数,如果两个矩阵相乘,就是i到j经过两条路径的种类数,最后只要得到0到其他点经过路径条数为n的数量,所以就只需要第一行的值相加就行。

    然后为什么都要不是单词末尾。(不是很理解)现在的想法,因为最后的路径是由0开始一直走下来的,所以就不能经过单词末尾的节点,那些节点都是无效的。  

    (第一次学会用公式编辑器不过好像还是贼丑)

       然后建立这个大的转移矩阵,矩阵的(i,j)为结点i到结点j的直接路径的条数,然后跑一个矩阵快速幂。 

       最后把从0到sz-1结点的f(n)的值全部加起来就是答案了。

        对了这个题需要用long long

       下面是代码

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <iostream>
    #include <queue>
    #include <map>
    
    using namespace std;
    const int maxnode=1100;
    const int MOD=100000;
    map<char,int>M;
    struct AC_Automata{
        int ch[maxnode][4],f[maxnode],last[maxnode],val[maxnode],match[maxnode];
        int sz;
        int idx(char c){
            return M[c];
        }
        void init(){
            sz=1;
            memset(ch[0],0,sizeof(ch[0]));
            memset(match,0,sizeof(match));
            val[0]=0;
        }
        void insert(char *s){
            int n=strlen(s),u=0;
            for(int i=0;i<n;i++){
                int c=idx(s[i]);
                if(!ch[u][c]){
                    ch[u][c]=sz;
                    memset(ch[sz],0,sizeof(ch[sz]));
                    val[sz++]=0;
                }
                u=ch[u][c];
            }
            val[u]=1;
            match[u]=1;
        }
        void getFail(){
            queue<int>q;
            last[0]=f[0]=0;
            for(int i=0;i<4;i++){
                int u=ch[0][i];
                if(u){
                    q.push(u);
                    f[u]=last[u]=0;
                }
            }
            while(!q.empty()){
                int r=q.front();q.pop();
                for(int i=0;i<4;i++){
                    int u=ch[r][i];
                    if(!u){
                        ch[r][i]=ch[f[r]][i];
                        continue;
                    }
                    q.push(u);
                    int v=f[r];
                    while(v&&!ch[v][i])v=f[v];
                    f[u]=ch[v][i];
                    match[u]|=match[f[u]];
                }
            }
        }
    }ac;
    const int maxN=101;
    struct Matrix{
        long long a[maxN][maxN];
        void init(){
            memset(a,0,sizeof(a));
            for(int i=0;i<ac.sz;i++)
                a[i][i]=1;
        }
    };
    Matrix mul(Matrix a,Matrix b){
        Matrix res;
        for(int i=0;i<ac.sz;i++){
            for(int j=0;j<ac.sz;j++){
                res.a[i][j]=0;
                for(int k=0;k<ac.sz;k++){
                    res.a[i][j]+=a.a[i][k]*b.a[k][j];
                    res.a[i][j]%=MOD;
                }
            }
        }
        return res;
    }
    Matrix qpow(Matrix a,int k){
        Matrix res;
        res.init();
        while(k){
            if(k%2)res=mul(res,a);
            a=mul(a,a);
            k/=2;
        }
        return res;
    }
    int n,m;
    char s[30];
    
    int main(){
        M['A']=0,M['C']=1,M['T']=2,M['G']=3;
        ac.init();
        scanf("%d%d",&m,&n);
        for(int i=1;i<=m;i++){
            scanf("%s",s);
            ac.insert(s);
        }
        ac.getFail();
        Matrix A;
        for(int i=0;i<ac.sz;i++){
                if(!ac.match[i])
            for(int j=0;j<4;j++){
                int u=ac.ch[i][j];
                if(!ac.match[u])
                A.a[i][u]++;
            }
        }
        /*for(int i=0;i<ac.sz;i++){
            for(int j=0;j<ac.sz;j++){
                printf("%d ",A.a[i][j]);
            }
            printf("
    ");
        }*/
    
        Matrix S;
        S.a[0][0]=1;
        Matrix ANS;
        ANS=qpow(A,n);
        ANS=mul(S,ANS);
        long long ans=0; 
        for(int i=0;i<=ac.sz;i++)
            ans+=ANS.a[0][i];
        printf("%d
    ",ans%MOD);
    return 0;
    }

    蓝书上217面的题,ac自动机+概率dp,用的记忆化搜索。

    给出一些字符和各自对应的选择概率,随机选择L次后将得到一个长度为L的随机字符串S(每次随机独立)。给出K个模式串,计算S不包含任何一个串的概率(即任何一个模板串都不是S的连续子串)

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    using namespace std;
    
    const int SIGMA_SIZE = 64;
    const int MAXNODE = 500;
    
    int idx[256];
    char s[30][30];
    double prob[SIGMA_SIZE];
    int n;
    
    
    struct AhoCorasickAutomata
    {
        int ch[MAXNODE][SIGMA_SIZE];
        int f[MAXNODE];
        int last[MAXNODE];
        int match[MAXNODE];
        int sz;
    
        void init()
        {
            sz = 1;
            memset(ch[0],0,sizeof(ch[0]));
        }
    //把模式串建立一颗字典树
        void insert(char *s)
        {
            int u = 0,n = strlen(s);
            for(int i=0; i<n; i++)
            {
                int c = idx[s[i]];
                if(!ch[u][c])
                {
                    memset(ch[sz],0,sizeof(ch[sz]));
                    match[sz] = 0;
                    ch[u][c] = sz++;
                }
                u = ch[u][c];
            }
            match[u] = 1;
        }
    //根据bfs得到fail指针与last指针,在字典树上还增加失配边,及ch[u][i]的值变化了,有些本来是零的,根据fail数组的值,可能与之后匹配到的边连在了一起。
        void getFail()
        {
            queue<int> q;
            f[0] = 0;
    //与根节点相连的边,先初始化一下,并把有的字母push进来。
    for(int c=0; c<SIGMA_SIZE; c++) { int u = ch[0][c]; if(u) { f[u] = 0; q.push(u); last[u] = 0; } } while(!q.empty()) { int r = q.front(); q.pop(); for(int c=0; c<SIGMA_SIZE; c++) { int u = ch[r][c];
    //代替了最上面那个模板的while循环,每次下去一层就根据fail指针来增加失配边。
    if(!u) { ch[r][c]=ch[f[r]][c]; continue; } q.push(u); int v = f[r]; // while(v&&!ch[v][c]) // v = f[v];
    f[u] = ch[v][c]; last[u] = match[f[u] ] ? f[u] : last[f[u] ];
    //这个是看这个节点是否是一个模式串的末尾,如果根据last匹配到的点是末尾,那么这个点也是某个模式串的末尾,因为last指针要么指向根节点,要么指向一个与当前匹配有相同后缀的点,如果是last节点的话,last匹配
    //后没有字符,但是fail指针后面可能会有字符。 match[u]
    |=match[last[u]];
    //这里相当于记录这个点是否是一个字符串的 } } } }; AhoCorasickAutomata ac;
    double d[MAXNODE][105]; int vis[MAXNODE][105]; double getProb(int u,int l) { if(!l) return 1.0; if(vis[u][l]) return d[u][l]; vis[u][l] = 1; double& ans = d[u][l]; ans = 0.0; for(int i=0; i<n; i++) { if(!ac.match[ac.ch[u][i]]) ans += prob[i]*getProb(ac.ch[u][i],l-1); } return ans; } int main() { int T; scanf("%d",&T); for(int kase = 1; kase<=T; kase ++) { int k,l; scanf("%d",&k); for(int i=0; i<k; i++) scanf("%s",s[i]); scanf("%d",&n); for(int i=0; i<n; i++) { char ch[9]; scanf("%s%lf",ch,&prob[i]); idx[ch[0]] = i; } ac.init(); for(int i=0; i<k; i++) ac.insert(s[i]); ac.getFail(); scanf("%d",&l); memset(vis,0,sizeof(vis)); printf("Case #%d: %.6lf ",kase,getProb(0,l)); } return 0; }

     DNA repair

     HDU - 2457 

    ac自动机+dp;

    跟矩阵那题思想差不多,只是这题要用dp来求最小值。

    题意:有些DNA序列是致病的,给你以个长DNA序列,问最少需要修改多少次使得其不含致病序列。

    首先是进行了一步转换,将其理解为在建好的Trie图上走len(序列长度)步,走的路不能包含致病序列。

    然后就变成了一个dp,d[i][j]表示第i步走到j节点最少需要修改几次。那么要看所有能走到j节点的点,如果和s[i-1]相同,就不用修改,如果不同,修改次数加一,取较小的。

    #include <bits/stdc++.h>
    using namespace std;
    #define CLR(m,a) memset(m,a,sizeof(m))
    const int inf=0x3f3f3f3f;
    const int maxnode=50*20+10;
    const int sigma=4;
    
    struct AC{
        int ch[maxnode][sigma],f[maxnode];
        int match[maxnode];
        int sz;
        int mp[128];
    
        void init(){
            CLR(match,0);
            mp['A']=0;mp['G']=1;mp['C']=2;mp['T']=3;
            CLR(ch[0],0);
            sz=1;
        }
        void inser(char *s){
            int u=0,n=strlen(s);
            for(int i=0;i<n;i++){
                int c=mp[s[i]];
                if(!ch[u][c]){
                    CLR(ch[sz],0);
                    ch[u][c]=sz++;
                }
                u=ch[u][c];
            }
            match[u]=1;
        }
        void getfail(){
            queue<int> q;
            f[0]=0;
            match[0]=0;
            for(int c=0;c<sigma;c++){
                int u=ch[0][c];
                if(u){
                    q.push(u);
                    f[u]=0;
                }
            }
            while(!q.empty()){
                int r=q.front();
                q.pop();
                for(int c=0;c<sigma;c++){
                    int u=ch[r][c];
                    if(!u){
                        ch[r][c]=ch[f[r]][c];
                        continue;
                    }
                    q.push(u);
                    int v=f[r];
                    while(v&&!ch[v][c]) v=f[v];
                    f[u]=ch[v][c];
                    match[u]|=match[f[u]];
                }
            }
        }
    };
    AC ac;
    char s[maxnode];
    int len;
    int dp[maxnode][maxnode];
    
    void DP(){
        CLR(dp,inf);
        dp[0][0]=0;
        for(int i=1;i<=len;i++){
            for(int j=0;j<ac.sz;j++){
                if(ac.match[j]) continue;
                for(int c=0;c<sigma;c++){
                    if(ac.match[ac.ch[j][c]]) continue;
                    dp[i][ac.ch[j][c]]=min(dp[i][ac.ch[j][c]],dp[i-1][j]+(ac.mp[s[i-1]]!=c));
                }
            }
        }
    }
    int main(){
        int n;
        int kase=0;
        while(scanf("%d",&n)!=EOF&&n){
            ac.init();
            for(int i=0;i<n;i++){
                scanf("%s",s);
                ac.inser(s);
            }
            ac.getfail();
            scanf("%s",s);
            len=strlen(s);
            DP();
            int ans=inf;
            for(int i=0;i<ac.sz;i++)if(!ac.match[i]){
                ans=min(dp[len][i],ans);
            }
            if(ans==inf) ans=-1;
            printf("Case %d: %d
    ",++kase,ans);
        }
        return 0;
    }
  • 相关阅读:
    CSS Tab简洁版,切换标签
    浮动在网页右侧的简洁QQ在线客服
    Marquee 最简单图片滚动特效
    浮动的图片广告
    Button控件设置不能点击
    Android requires compiler compliance level 5.0 or 6.0. Found '1.4' instead的解决办法
    BroadcastReceiver组件
    发邮件 Async="true"
    ASP.NET GridView,DataList,Repeater日期格式显示
    Json原理和语法
  • 原文地址:https://www.cnblogs.com/downrainsun/p/10898327.html
Copyright © 2011-2022 走看看