[HNOI2006]最短母串 状压DP

本文探讨了如何使用状态压缩动态规划(状压DP)解决寻找n个字符串的最短公共超串问题。通过预处理字符串重叠部分,结合字典序比较,实现高效算法设计。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description
给定n个字符串(S1,S2,„,Sn),要求找到一个最短的字符串T,使得这n个字符串(S1,S2,„,Sn)都是T的子串。


Sample Input
2
ABCD
BCDABC


Sample Output
ABCDABC


考虑状压DP。
先去一下重。
然后预处理两个串的最大重叠。
然后就是字典序的大小,于是你每次都要back回去。。。
有点麻烦。
strcmp返回大于0的值表示大于。。。


#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
typedef long long LL;
int _min(int x, int y) {return x < y ? x : y;}
int _max(int x, int y) {return x > y ? x : y;}
int read() {
	int s = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
	return s * f;
}

int f[13][4096], last[13][4096];
int bs[13][13], len[13];
char ss[13][51], qq[31][51], hh[610], ll[610];

bool check(int a, int b, int c, int d) {
	int tp1 = 0;
	for(int i = len[a]; i; i--) hh[++tp1] = qq[a][i];
	int pos = a, now = b;
	while(pos) {
		int u = last[pos][now];
		for(int j = len[u] - bs[u][pos]; j; j--) hh[++tp1] = qq[u][j];
		now ^= 1 << pos - 1, pos = u;
	} for(int i = 1; i <= tp1 / 2; i++) swap(hh[i], hh[tp1 - i + 1]);
	hh[tp1 + 1] = '\0';
	int tp2 = 0;
	for(int i = len[a]; i; i--) ll[++tp2] = qq[a][i];
	for(int i = len[c] - bs[c][a]; i; i--) ll[++tp2] = qq[c][i];
	pos = c, now = d;
	while(pos) {
		int u = last[pos][now];
		for(int j = len[u] - bs[u][pos]; j; j--) ll[++tp2] = qq[u][j];
		now ^= 1 << pos - 1, pos = u;
	} for(int i = 1; i <= tp2 / 2; i++) swap(ll[i], ll[tp2 - i + 1]);
	ll[tp2 + 1] = '\0';
	if(strcmp(hh + 1, ll + 1) > 0) return 1;
	return 0;
}

int main() {
	int n = read(), tp = 0;
	for(int i = 1; i <= n; i++) scanf("%s", ss[i] + 1);
	for(int i = 1; i <= n; i++) {
		bool bk = 0; int len = strlen(ss[i] + 1);
		for(int j = 1; j <= n; j++) if(i != j){
			for(int k = 1; k <= strlen(ss[j] + 1); k++) {
				bool hh = 0;
				for(int u = 1; u <= len; u++) {
					if(ss[j][u + k - 1] != ss[i][u]) {hh = 1; break;}
				} if(!hh) {bk = 1; break;}
			} if(bk) break;
		} if(!bk) {
			++tp;
			for(int j = 1; j <= strlen(ss[i] + 1); j++) qq[tp][j] = ss[i][j];
		}
	} n = tp;
	if(n == 0) {
		printf("%s\n", ss[1] + 1);
		return 0;
	}
	for(int i = 1; i <= n; i++) {
		len[i] = strlen(qq[i] + 1);
		for(int j = 1; j <= n; j++) if(i != j){
			int yy = strlen(qq[j] + 1);
			for(int k = 1; k <= len[i]; k++) {
				int uu = yy + 1;
				for(int u = 1; u <= yy; u++) {
					if(qq[i][k + u - 1] != qq[j][u]) {uu = u; break;}
				} uu--;
				if(k + uu - 1 == len[i]) bs[i][j] = _max(bs[i][j], uu);
			}
		}
	} memset(f, -1, sizeof(f));
	for(int i = 1; i <= n; i++) f[i][1 << i - 1] = len[i];
	for(int i = 1; i < 2046; i++) {
		for(int j = 1; j <= n; j++) if(f[j][i] != -1){
			for(int k = 1; k <= n; k++) if(!(i >> (k - 1) & 1)){
				if(f[k][i ^ (1 << k - 1)] == -1)
				f[k][i ^ (1 << k - 1)] = f[j][i] + len[k] - bs[j][k], last[k][i ^ (1 << k - 1)] = j;
				else if(f[k][i ^ (1 << k - 1)] > f[j][i] + len[k] - bs[j][k]){
					f[k][i ^ (1 << k - 1)] = f[j][i] + len[k] - bs[j][k];
					last[k][i ^ (1 << k - 1)] = j;
				} else if(f[k][i ^ (1 << k - 1)] == f[j][i] + len[k] - bs[j][k]) {
					if(check(k, i ^ (1 << k - 1), j, i)) last[k][i ^ (1 << k - 1)] = j;
				}
			}
		}
	}
	for(int i = 2046; i < (1 << n); i++) {
		for(int j = 1; j <= n; j++) if(f[j][i] != -1){
			for(int k = 1; k <= n; k++) if(!(i >> (k - 1) & 1)){
				if(f[k][i ^ (1 << k - 1)] == -1)
				f[k][i ^ (1 << k - 1)] = f[j][i] + len[k] - bs[j][k], last[k][i ^ (1 << k - 1)] = j;
				else if(f[k][i ^ (1 << k - 1)] > f[j][i] + len[k] - bs[j][k]){
					f[k][i ^ (1 << k - 1)] = f[j][i] + len[k] - bs[j][k];
					last[k][i ^ (1 << k - 1)] = j;
				} else if(f[k][i ^ (1 << k - 1)] == f[j][i] + len[k] - bs[j][k]) {
					if(check(k, i ^ (1 << k - 1), j, i)) last[k][i ^ (1 << k - 1)] = j;
				}
			}
		}
	}
	int ans = 999999999;
	for(int i = 1; i <= n; i++) {
		if(f[i][(1 << n) - 1] != -1 && f[i][(1 << n) - 1] < ans) ans = f[i][(1 << n) - 1];
	} memset(hh, 0, sizeof(hh)), memset(ll, 0, sizeof(ll));
	int g;
	for(int i = 1; i <= n; i++) if(f[i][(1 << n) - 1] == ans){
		int pos = i, now = (1 << n) - 1;
		int tp = 0; for(int j = len[i]; j; j--) ll[++tp] = qq[i][j];
		while(pos) {
			int u = last[pos][now];
			for(int j = len[u] - bs[u][pos]; j; j--) ll[++tp] = qq[u][j];
			now ^= 1 << pos - 1, pos = u;
		} ll[ans + 1] = '\0', hh[ans + 1] = '\0';
		for(int j = 1; j <= ans / 2; j++) swap(ll[j], ll[ans - j + 1]);
		if(hh[1] == 0) {
			for(int j = 1; j <= ans; j++) hh[j] = ll[j]; g = i;
		} else if(strcmp(hh + 1, ll + 1) > 0) swap(hh, ll), g = i;
	} int o = strlen(hh + 1);
	printf("%s\n", hh + 1);
	return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值