[BZOJ1195][HNOI2006]最短母串(状压DP)

首先,去除一些没有用的串。
也就是说,如果存在两个串 si sj 使 sj si 的子串,那么需要去除掉 sj (因为包含串 si 就一定包含串 sj )。但是要注意考虑特殊情况:如果存在一个字符串集合 S S里的字符串全部相等,那么 S 里的串在不作为其他串的子串的情况下必须保留一个。一定要去除的原因,在DP部分会详细介绍。
然后预处理出cnt[i][j],表示使 si 的长度为 k 后缀sj的长度为 k 前缀相等的最大的k值,实现见代码。
DP模型: f[S][i] 表示已经选的字符串集合为 S ,最后一个串为si的最优字符串。由于 S 中不包含其他i的子串(预处理中已经去除掉了),所以可以枚举前一个字符串,利用预处理出的 cnt 进行转移(可以推出,如果不去除没有用的串,那么这个DP有后效性)。
代码(微恶心):

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 14, M = 53, R = 605, C = (1 << 12) + 5, INF = 0x3f3f3f3f;
int n, nxt[M], len[N], tmp[N], f[C][N], con[N][N];
char s[N][M], ts[N][M];
bool vis[N]; string st[N][M], str[C][N];
bool check(int x, int y) {
    int i, j = 0; nxt[1] = 0;
    for (i = 2; i <= len[y]; i++) {
        while (j && s[y][i] != s[y][j + 1]) j = nxt[j];
        if (s[y][i] == s[y][j + 1]) j++;
        nxt[i] = j;
    }
    j = 0; for (i = 1; i <= len[x]; i++) {
        while (j && s[x][i] != s[y][j + 1]) j = nxt[j];
        if (s[x][i] == s[y][j + 1]) j++;
        if (j == len[y]) return 1;
    }
    return 0;
}
bool check2(int x, int y, int p) {
    int i; for (i = len[x] - p + 1; i <= len[x]; i++)
        if (s[x][i] != s[y][i + p - len[x]]) return 0;
    return 1;
}
void DP() {
    int i, j, k, Cm = 1 << n; for (i = 1; i < Cm; i++)
    for (j = 1; j <= n; j++) {
        if (!((i >> j - 1) & 1)) continue;
        if (!(i & (i - 1))) {
            f[i][j] = len[j]; str[i][j] = st[j][1];
            continue;
        }
        f[i][j] = INF; string tm(600, 'Z'); str[i][j] = tm;
        for (k = 1; k <= n; k++) {
            if ((!((i >> k - 1) & 1)) || j == k) continue;
            int S = i ^ (1 << j - 1), le = f[S][k] + len[j] - con[k][j];
            if (le < f[i][j] || (le == f[i][j] &&
                (str[S][k] + st[j][con[k][j] + 1]) < str[i][j]))
                    f[i][j] = le, str[i][j] =
                        str[S][k] + st[j][con[k][j] + 1];
        }
    }
    int res = INF; string zz(600, 'Z');
        for (i = 1; i <= n; i++)
            if (f[Cm - 1][i] < res || (f[Cm - 1][i] == res &&
                str[Cm - 1][i] < zz)) res = f[Cm - 1][i], zz = str[Cm - 1][i];
    cout << zz << endl;
}
int main() {
    int i, j, k, tot = 0; scanf("%d", &n);
    for (i = 1; i <= n; i++) scanf("%s", s[i] + 1), len[i] = strlen(s[i] + 1);
    for (i = 1; i <= n; i++) for (j = 1; j <= n; j++) {
        if (i == j) continue;
        if (check(i, j) && (len[i] != len[j] || (len[i] == len[j] && i < j)))
            vis[j] = 1;
    }
    for (i = 1; i <= n; i++) {
        if (vis[i]) continue; tot++;
        for (j = 1; j <= len[i]; j++) ts[tot][j] = s[i][j];
        tmp[tot] = len[i];
    }
    n = tot; for (i = 1; i <= n; i++) {
        for (j = 1; j <= tmp[i]; j++) s[i][j] = ts[i][j];
        len[i] = tmp[i];
    }
    for (i = 1; i <= n; i++) for (j = 1; j <= n; j++)
    for (k = 1; k < min(len[i], len[j]); k++)
        if (check2(i, j, k)) con[i][j] = k;
    for (i = 1; i <= n; i++) for (j = 1; j <= len[i]; j++) {
        string tm(len[i] - j + 1, 'A');
        for (k = j; k <= len[i]; k++) tm[k - j] = s[i][k];
        st[i][j] = tm;
    }
    return DP(), 0;
}

此外,此题的AC自动机解法参考链接:http://wyfcyx.is-programmer.com/posts/76627.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值