其他分享
首页 > 其他分享> > ac状态机以及后缀树

ac状态机以及后缀树

作者:互联网

DNA修复

看提交记录(有注释)

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 1010;

int n, m;
int tr[N][4], dar[N], idx;
int q[N], ne[N];
char str[N];

int f[N][N];

int get(char c)
{
    if (c == 'A') return 0;
    if (c == 'T') return 1;
    if (c == 'G') return 2;
    return 3;
}

void insert()  //插入字符串,是建立一般的树的过程
{
    int p = 0;
    for (int i = 0; str[i]; i ++ )   
    {
        int t = get(str[i]); 
        if (tr[p][t] == 0) tr[p][t] = ++ idx; //这里必须从1开始,因为初始点是0,是空的root
        p = tr[p][t];     
    }
    dar[p] = 1;   //标记该点是dargerous
}

void build()  //将树完整化,变成后缀树,也就是后缀自动机,每个点代表一个状态,每条边代表一种方式
{
    int hh = 0, tt = -1;          //用队列实现
    for (int i = 0; i < 4; i ++ )
        if (tr[0][i])
            q[ ++ tt] = tr[0][i]; //初始化队头

    while (hh <= tt)              //完整化树,按照层建立,因为是要按照字符多少便利的,层数就是字符多少
    {
        int t = q[hh ++ ];        
        for (int i = 0; i < 4; i ++ )   
        { 
            int p = tr[t][i];     //子节点(一个待转移的状态)
            if (!p) tr[t][i] = tr[ne[t]][i];  //如果是空的,就代表要进行完善了,把它的最长后缀变成它的子节点
            else                  //如果不空
            {                             
                ne[p] = tr[ne[t]][i];    //更新该点的fail值
                q[ ++ tt] = p;           //入队
                dar[p] |= dar[ne[p]];    //如果有后缀是dar,自己也是dar
            }
        }
    }
}

int main()
{
    int T = 1;
    while (scanf("%d", &n), n)
    {
        memset(tr, 0, sizeof tr);
        memset(dar, 0, sizeof dar);
        memset(ne, 0, sizeof ne);
        idx = 0;

        for (int i = 0; i < n; i ++ )
        {
            scanf("%s", str);
            insert();
        }

        build();

        scanf("%s", str + 1);
        m = strlen(str + 1);  //字符串从1开始

        memset(f, 0x3f, sizeof f);
        f[0][0] = 0;  //修改到第i个字符,当前在状态机中是j时的最小需要修改次数
        for (int i = 0; i < m; i ++ )
            for (int j = 0; j <= idx; j ++ )   //枚举各个状态
                for (int k = 0; k < 4; k ++ )  //枚举到下一个状态的转移途径
                {
                    int t = get(str[i + 1]) != k;   //未匹配
                    int p = tr[j][k];               //可能是fail[], 也可能不是,这里对树进行了修改,使得下面就是fail
                    if (!dar[p]) f[i + 1][p] = min(f[i + 1][p], f[i][j] + t);  //下个状态可以达到
                }

        int res = 0x3f3f3f3f;
        for (int i = 0; i <= idx; i ++ ) res = min(res, f[m][i]);

        if (res == 0x3f3f3f3f) res = -1;
        printf("Case %d: %d\n", T ++, res);
    }

    return 0;
}

标签:ac,return,后缀,tr,++,状态机,int,str,include
来源: https://www.cnblogs.com/sherkevin/p/16390561.html