HDU 2457 -- AC自动机+dp

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2457

题目大意:给你一些Bug字符组,再给你一个长字符串,问要使长字符串中不出现这些BUG字符组,需要至少改动几个字符

思路:这道题AC自动机部分我会,直接模板敲上去记录一下端点就完事了,然后dp部分我又实在是不会,好烦~~

这里说一些其他大佬的dp思路:用DP[i][j]表示长度为i (i <= 1000),状态为j(j <= 50*20 + 1)的字符串变成目标串S需要改变的最少字符,设初始状态j = 0,那么DP[0][0] = 0,其他均为无穷大。从长度i到i+1进行状态转移,每次转移枚举共四个字符(A、C、G、T),如果枚举到的字符和S对应位置相同则改变值T=1,否则T=0;那么有状态转移方程 DP[i][j] = Min{ DP[i-1][ fromstate ] + T, fromstate为所有能够到达j的状态 };最后DP[n][j]中的最小值就是答案

贴一下AC代码吧:

#include <bits/stdc++.h>
using namespace std;

const int INF = 0X3f3f3f3f;
char str[1005];
struct Trie
{
    int next[1005][4], fail[1005];
    bool last[1005];
    int root, L;
    int newnode()
    {
        for(int i = 0; i < 4; ++i)
            next[L][i] = -1;
        last[L++] = false;
        return L-1;
    }
    void init()
    {
        L = 0;
        root = newnode();
    }
    int getch(char ch)
    {
        if(ch == 'A')   return 0;
        else if(ch == 'G')  return 1;
        else if(ch == 'C')  return 2;
        else if(ch == 'T')  return 3;
    }
    void insert(char str[])
    {
        int len = strlen(str), now = root;
        for(int i = 0; i < len; ++i){
            if(next[now][getch(str[i])] == -1)
                next[now][getch(str[i])] = newnode();
            now = next[now][getch(str[i])];
        }
        last[now] = true;
    }
    void build()
    {
        queue<int> q;
        fail[root] = root;
        for(int i = 0; i < 4; ++i){
            if(next[root][i] == -1)
                next[root][i] = root;
            else{
                fail[next[root][i]] = root;
                q.push(next[root][i]);
            }
        }
        while(!q.empty()){
            int now = q.front();
            q.pop();
            if(last[fail[now]]) last[now] = true;
            for(int i = 0; i < 4; ++i){
                if(next[now][i] == -1)
                    next[now][i] = next[fail[now]][i];
                else{
                    fail[next[now][i]] = next[fail[now]][i];
                    q.push(next[now][i]);
                }
            }
        }
    }
    int dp[1005][1005];
    int solve(char str[])
    {
        int len = strlen(str);
        for(int i = 0; i <= len; ++i){
            for(int j = 0; j < L; ++j)
                dp[i][j] = INF;
        }
        dp[0][root] = 0;
        for(int i = 0; i < len; ++i){
            for(int j = 0; j < L; j++){
                if(dp[i][j] < INF){
                    for(int k = 0; k < 4; ++k){
                        int news = next[j][k];
                        if(last[news])  continue;
                        int tmp;
                        if(k == getch(str[i]))  tmp = dp[i][j];
                        else    tmp = dp[i][j]+1;
                        dp[i+1][news] = min(dp[i+1][news], tmp);
                    }
                }
            }
        }
        int ans = INF;
        for(int j = 0; j < L; j++)
            ans = min(ans, dp[len][j]);
        if(ans == INF)  ans = -1;
        return ans;
    }
}trie;
int main()
{
    int n, cas = 0;
    while(~scanf("%d", &n)){
        if(n == 0)  break;
        cas++;
        trie.init();
        while(n--){
            scanf("%s", str);
            trie.insert(str);
        }
        trie.build();
        scanf("%s", str);
        printf("Case %d: %d\n", cas, trie.solve(str));
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值