hdu 3341(ac自动机+dp)

题意:给出一个n个模式串,一个目标串,问把目标串重新排位最多能产生多少个模式串,可以重叠且所有串只包含A C G T。
题解:先统计目标串里A C G T的个数,题目意思转化为用num[0]个A,num[1]个C,num[2]个G,num[3]个T组成的串最多包含多少个模式串。可以先建好trie图,根据trie图然后dp,重点在状态的转移,f[i][s],表示i节点状态是s的最优解,状态可以用hash值表示,hash[i][j][k][l]表示当前有i个A,j个C,k个G,l个T的情况下的状态值,四层循环给每种情况一个值。状态转移方程如果当前字母是a,f[next[i][0]][hash[a][b][c][d]] = max(f[next[i][0]][a][b][c][d], f[i][hash[a - 1][b][c][d] + val[next[i][0]])。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
using namespace std;
const int N = 505;
int Next[N][4], val[N], fail[N], sz, n;
int num[4], Hash[41][41][41][41], f[N][15000];
char str[N];
map <char, int> mp;

void init() {
    memset(Next[0], 0, sizeof(Next[0]));
    val[0] = 0;
    sz = 1;
}

void insert(char *s) {
    int u = 0, len = strlen(s);
    for (int i = 0; i < len; i++) {
        int k = mp[s[i]];
        if (!Next[u][k]) {
            memset(Next[sz], 0, sizeof(Next[sz]));
            val[sz] = 0;
            Next[u][k] = sz++;
        }
        u = Next[u][k];
    }
    val[u]++;
}

void getFail() {
    queue<int> Q;
    fail[0] = 0;
    for (int i = 0; i < 4; i++)
        if (Next[0][i]) {
            fail[Next[0][i]] = 0;
            Q.push(Next[0][i]);
        }
    while (!Q.empty()) {
        int u = Q.front();
        Q.pop();
        val[u] += val[fail[u]];
        for (int i = 0; i < 4; i++) {
            if (!Next[u][i])
                Next[u][i] = Next[fail[u]][i];
            else {
                fail[Next[u][i]] = Next[fail[u]][i];
                Q.push(Next[u][i]);
            }
        }
    }
}

int main() {
    mp['A'] = 0;
    mp['C'] = 1;
    mp['G'] = 2;
    mp['T'] = 3;
    int cas = 1;
    while (scanf("%d", &n) == 1 && n) {
        init();
        memset(num, 0, sizeof(num));
        memset(f, -1, sizeof(f));
        for (int i = 0; i < n; i++) {
            scanf("%s", str);
            insert(str);
        }
        getFail();
        scanf("%s", str);
        int len = strlen(str);
        for (int i = 0; i < len; i++)
            num[mp[str[i]]]++;
        int cnt = 0;
        for (int i = 0; i <= num[0]; i++)
            for (int j = 0; j <= num[1]; j++)
                for (int k = 0; k <= num[2]; k++)
                    for (int l = 0; l <= num[3]; l++)
                        Hash[i][j][k][l] = cnt++;
        f[0][0] = 0;
        int res = 0;
        for (int a = 0; a <= num[0]; a++)
            for (int c = 0; c <= num[1]; c++)
                for (int g = 0; g <= num[2]; g++)
                    for (int t = 0; t <= num[3]; t++) {
                        int h1 = Hash[a][c][g][t];
                        if (a + c + g + t == 0)
                            continue;
                        for (int i = 0; i < sz; i++)
                            for (int j = 0; j < 4; j++) {
                                int h2;
                                if (j == 0 && a >= 1)
                                    h2 = Hash[a - 1][c][g][t];
                                else if (j == 1 && c >= 1)
                                    h2 = Hash[a][c - 1][g][t];
                                else if (j == 2 && g >= 1)
                                    h2 = Hash[a][c][g - 1][t];
                                else if (j == 3 && t >= 1)
                                    h2 = Hash[a][c][g][t - 1];
                                else
                                    continue;
                                if (f[i][h2] == -1)
                                    continue;
                                f[Next[i][j]][h1] = max(f[Next[i][j]][h1], f[i][h2] + val[Next[i][j]]);
                                res = max(res, f[Next[i][j]][h1]);
                            }
                    }
        printf("Case %d: %d\n", cas++, res);
    }
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值