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

来源:互联网 发布:java velocity 编辑:程序博客网 时间:2024/06/04 19:14

首先,去除一些没有用的串。
也就是说,如果存在两个串sisj使sjsi的子串,那么需要去除掉sj(因为包含串si就一定包含串sj)。但是要注意考虑特殊情况:如果存在一个字符串集合SS里的字符串全部相等,那么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。

原创粉丝点击