hdu2457

hdu 2457
题意:
依旧是DNA序列的问题。给出一些致病的基因片段(仅由’A’,’G’,’C’,’T’组成),和一个DNA序列T,可以在序列T上做修改操作,修改某一个碱基为另一个碱基记作一次修改操作。问你最少修改多少次使得序列T不包含任何已给出的致病基因片段。
思路:
多串匹配,依旧是AC自动机。关于trie图与DNA序列的关系可以参考poj 1625
接下来是dp的部分
题目要求最少修改次数。DNA序列的形成可以理解为在trie图上走n步的情况。设计状态dp[i][j]表示长度为i的串在节点j时所需的最少修改次数。由Trie图的性质可知,第j->next[k]的状态节点必定由j节点转移而来。也就是说dp[i][j]与所有dp[i-1][k](k到j有一条有向边)有关。
他们之间的关系即dp转移方程是dp[i][j->next[k]] = min(dp[i][j->next[k]],dp[i-1][j]+s[i-1]!='k')
这个方程该如何理解呢?
题目中的修改操作以及不修改操作可以统一为trie图上沿着有向边的状态转移,只不过转出的节点不同且修改操作需要dp值加一,而对于每个状态dp[i][j->next[k]]的值是所有dp[i-1][j]+t(若s[i-1]即长度为i的串的最后一个字符也就是当前匹配的字符==k在字符集中对应的字符(对于此题,k=0,1,2,3分别对应于’A’,’G’,’C’,’T’),t=0;否则t=1)的最小值。
换一种方法来说,dp[i][j]是所有(dp[i-1][k](k到j在trie图上有一条有向边)+k到j转移所需的修改次数(取值为0或1))的最小值。
那么最后的ans=min(dp[slen][m])(0<=m
经过分析,dp数组初始化为inf(即设定的某个最大值。若dp初值为0,则违背了dp数组的含义,表示任意长度字串到任意状态需要的修改次数均为0)。dp[0][0]=0dp的起点也就是空串。
dp状态第一维是字串长度,第二维是trie图上的节点,除根节点外,其他所有节点都表示当前字符串以该节点所代表的字符结尾。而根节点dp[0][0]表示空串,dp[i][0](i>0) 表示字串以字符集中的字符结尾,具体的字符取决于转移边所代表的字符,但一定不是空串。
也就是说,trie图根节点入边的字符可能是不确定的,而其他节点的入边所表示的字符是一定相同的。
注意事项:
危险节点,也就是原trie图中的致病基因结尾节点要删除。中间节点的last[]若不为根节点,也要删除。
下面贴代码:

#include 
#include 
#include 
#define maxnode 1005
#define sigma_size 4
#define length 1005
#define inf 2000
using namespace std;

int ch[maxnode][sigma_size];
int val[maxnode];
int f[maxnode];
int last[maxnode];
int sz;

int dp[length][maxnode];
char ban[25];
char s[length];
int n;
int slen;

char charset[] = {'A', 'G', 'C', 'T'};

void initial()
{
    memset(ch[0], 0, sizeof(ch[0]));
    sz = 1;
}

int getch(char a)
{
    switch(a)
    {
    case 'A':
        return 0;
    case 'G':
        return 1;
    case 'C':
        return 2;
    case 'T':
        return 3;
    }
}
void insert(char *T)
{
    int l = strlen(T);
    int u = 0;
    for(int i = 0; i < l; i++)
    {
        int c = getch(T[i]);
        if(!ch[u][c])
        {
            memset(ch[sz], 0, sizeof(ch[sz]));
            val[sz] = 0;
            ch[u][c] = sz++;
        }
        u = ch[u][c];
    }
    val[u] = 1;
}

void getfail()
{
    queue <int> q;
    f[0] = 0;
    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];
            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] = val[f[u]] ? f[u] : last[f[u]];
        }
    }
}

void dps()
{
    for(int i = 0; i <= slen;i++)
        for(int j = 0; j < sz; j++)
            dp[i][j] = inf;
    dp[0][0] = 0;
    for(int i = 1; i <= slen; i++)
    {
        for(int j = 0; j < sz; j++)
        {
            if(val[j] || last[j])
            {
                continue;
            }
            for(int k = 0; k < 4; k++)
            {
                if(val[ch[j][k]] || last[ch[j][k]])
                    continue;
                int now = dp[i - 1][j];
                if(s[i - 1] != charset[k])
                    now += 1;
                if(now < dp[i][ch[j][k]])
                    dp[i][ch[j][k]] = now;
            }
        }
    }
}


int main()
{
    int tt = 0;
    while(~scanf("%d", &n) && n)
    {
        ++tt;
        initial();
        for(int i = 0; i < n; i++)
        {
            scanf("%s", ban);
            insert(ban);
        }
        getfail();
        scanf("%s", s);
        slen = strlen(s);
        dps();
        int ans = inf;
        for(int i = 0; i < sz; i++)
            if(dp[slen][i] < ans)
                ans = dp[slen][i];
        if(ans == inf)
            ans = -1;
        printf("Case %d: %d\n",tt,ans);
    }
    return 0;
}

你可能感兴趣的:(解题报告,dp,字符串)