pku 3691 DNA repair AC自动机——DP

本文介绍了一道经典的多串匹配题目——POJ 3691,通过构建Trie树和确定性有限状态自动机(DFA),结合动态规划求解最小编辑距离,以确保目标DNA片段不含有特定的病毒序列。

http://poj.org/problem?id=3691

题意:

给出n个DNA病毒串,然后给出一个需要修改的DNA片段,问需要最少修改多少个字符才能是该DNA片段不含DNA病毒串,修改后的DNA片段长度不变

思路:
这题看了一天了,DP那地方好难懂。首先这里是多串匹配,我们用Trie树和fail的构造确定性有限状态自动机(DFA),然后再DFA上进行DP;

这里我DP理解了很长时间,dp[i][j]表示主串匹配到了第i个位置,然后到达的是AC自动机上的j状态时修改字符的个数,我们保证j状态不是模式串(DNA病毒串)的结束节点,然后不断地往后走选出一条匹配完主串,并且修改字符串数最少的的一条。

dp[i + 1][H[j].next[k]->id] = min(dp[i + 1][H[j].next[k]->id],dp[i][j] + add);

//#pragma comment(linker,"/STACK:327680000,327680000")
#include <iostream>
#include <cstdio>
#include <cmath>
#include <vector>
#include <cstring>
#include <algorithm>
#include <string>
#include <set>
#include <functional>
#include <numeric>
#include <sstream>
#include <stack>
#include <map>
#include <queue>

#define CL(arr, val)    memset(arr, val, sizeof(arr))

#define lc l,m,rt<<1
#define rc m + 1,r,rt<<1|1
#define pi acos(-1.0)
#define ll long long
#define L(x)    (x) << 1
#define R(x)    (x) << 1 | 1
#define MID(l, r)   (l + r) >> 1
#define Min(x, y)   (x) < (y) ? (x) : (y)
#define Max(x, y)   (x) < (y) ? (y) : (x)
#define E(x)        (1 << (x))
#define iabs(x)     (x) < 0 ? -(x) : (x)
#define OUT(x)  printf("%I64d\n", x)
#define lowbit(x)   (x)&(-x)
#define Read()  freopen("din.txt", "r", stdin)
#define Write() freopen("dout.txt", "w", stdout);


#define M 23
#define N 1000007
using namespace std;

const int inf = 0x7f7f7f7f;
struct node
{
    node *next[4];
    node *fail;
    int cnt;
    int id;
    void newnode()
    {
        fail = NULL;
        cnt = 0;
        for (int i= 0; i < 4; ++i)
        {
            next[i] = NULL;
        }
    }
};

class Ac_automat
{
public:
    node *root,*q[N],H[N];
    int fr,tl;
    int t,dp[1007][1007];
    void init()
    {
        fr = tl = 0;
        t = 0;
        H[t].newnode();
        H[t].id = t;//对Trie树上的每一个节点给一个状态号
        root = &H[t++];
    }
    int getVal(char ch)
    {
        if (ch == 'A') return 0;
        else if (ch == 'G') return 1;
        else if (ch == 'C') return 2;
        else return 3;
    }
    void insert(char *s)
    {
        int i,k;
        int len = strlen(s);
        node *p = root;
        for (i = 0; i < len; ++i)
        {
            k = getVal(s[i]);
            if (p->next[k] == NULL)
            {
                H[t].newnode();
                H[t].id = t;//给定状态的编号
                p->next[k] = &H[t++];
            }
            p = p->next[k];
        }
        p->cnt = 1;
    }
    void build()
    {
        root->fail = NULL;
        q[tl] = root;
        while (fr <= tl)
        {
            node *tmp = q[fr++];
            //这里解决出现aact  aac这种情况,只要包含aac就是非法的的有病毒的
            if (tmp != root)
            tmp->cnt = tmp->cnt||tmp->fail->cnt;

            for (int i = 0; i < 4; ++i)
            {
                //这里要更新每一个next为空的指针,好循环匹配
                if (tmp->next[i] == NULL)
                {
                    if (tmp == root) tmp->next[i] = root;
                    else tmp->next[i] = tmp->fail->next[i];
                }
                else//这里还是构造匹配失败指针
                {
                    if (tmp == root) tmp->next[i]->fail = root;
                    else
                    {
                        node *p = tmp->fail;
                        while (p != NULL)
                        {
                            if (p->next[i])
                            {
                                tmp->next[i]->fail = p->next[i];
                                break;
                            }
                            p = p->fail;
                        }
                        if (p == NULL) tmp->next[i]->fail = root;
                    }
                    q[++tl] = tmp->next[i];
                }
            }
        }
    }
    void solve(char *s,int cas)
    {
        int i,j,k;
        int len = strlen(s);
        for (i = 0; i <= len; ++i)
        {
            for (j = 0; j < t; ++j)
            {
                dp[i][j] = inf;
            }
        }

        dp[0][0] = 0;//根节点开始
        for (i = 0; i < len; ++i)//走主串
        {
            for (j = 0; j < t; ++j)//枚举每一个状态
            {
                if (dp[i][j] != inf && H[j].cnt== 0)//该状态合法
                {
                    //printf(">>>>>\n");
                    for (k = 0; k < 4; ++k)//枚举可走的边
                    {
                        if (H[j].next[k]->cnt != 0) continue;
                        int add = (k != getVal(s[i]));
                        dp[i + 1][H[j].next[k]->id] = min(dp[i + 1][H[j].next[k]->id],dp[i][j] + add);
                    }
                }
            }
        }
        int ans = -1;
        for (i = 0; i < t; ++i)
        {
           // printf("%d\n",dp[len][i]);
            if (dp[len][i] != inf && (ans == -1 || dp[len][i] < ans)) ans = dp[len][i];
        }
        printf("Case %d: %d\n",cas++,ans);
    }
}ac;

int n;
char str[N],ts[1007];

int main()
{
    int i;
    int cas = 1;
    while (~scanf("%d",&n))
    {
        if (!n) break;
        ac.init();
        for (i = 0; i < n; ++i)
        {
            scanf("%s",str);
            ac.insert(str);
        }
        ac.build();
        scanf("%s",ts);
        ac.solve(ts,cas);
        cas++;
    }
    return 0;
}

  

 

 

转载于:https://www.cnblogs.com/E-star/archive/2013/02/20/2919301.html

内容概要:本文系统介绍了算术优化算法(AOA)的基本原理、核心思想及Python实现方法,并通过图像分割的实际案例展示了其应用价值。AOA是一种基于种群的元启发式算法,其核心思想来源于四则运算,利用乘除运算进行全局勘探,加减运算进行局部开发,通过数学优化器加速函数(MOA)和数学优化概率(MOP)动态控制搜索过程,在全局探索与局部开发之间实现平衡。文章详细解析了算法的初始化、勘探与开发阶段的更新策略,并提供了完整的Python代码实现,结合Rastrigin函数进行测试验证。进一步地,以Flask框架搭建前后端分离系统,将AOA应用于图像分割任务,展示了其在实际工程中的可行性与高效性。最后,通过收敛速度、寻优精度等指标评估算法性能,并提出自适应参数调整、模型优化和并行计算等改进策略。; 适合人群:具备一定Python编程基础和优化算法基础知识的高校学生、科研人员及工程技术人员,尤其适合从事人工智能、图像处理、智能优化等领域的从业者;; 使用场景及目标:①理解元启发式算法的设计思想与实现机制;②掌握AOA在函数优化、图像分割等实际问题中的建模与求解方法;③学习如何将优化算法集成到Web系统中实现工程化应用;④为算法性能评估与改进提供实践参考; 阅读建议:建议读者结合代码逐行调试,深入理解算法流程中MOA与MOP的作用机制,尝试在不同测试函数上运行算法以观察性能差异,并可进一步扩展图像分割模块,引入更复杂的预处理或后处理技术以提升分割效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值