[HNOI2006]最短母串 状压DP

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;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
根据引用\[1\]和引用\[2\]的描述,题目中的影魔拥有n个灵魂,每个灵魂有一个战斗力ki。对于任意一对灵魂对i,j (i<j),如果不存在ks (i<s<j)大于ki或者kj,则会为影魔提供p1的攻击力。另一种情况是,如果存在一个位置k,满足ki<c<kj或者kj<c<ki,则会为影魔提供p2的攻击力。其他情况下的灵魂对不会为影魔提供攻击力。 根据引用\[3\]的描述,我们可以从左到右进行枚举。对于情况1,当扫到r\[i\]时,更新l\[i\]的贡献。对于情况2.1,当扫到l\[i\]时,更新区间\[i+1,r\[i\]-1\]的贡献。对于情况2.2,当扫到r\[i\]时,更新区间\[l\[i\]+1,i-1\]的贡献。 因此,对于给定的区间\[l,r\],我们可以根据上述方法计算出区间内所有下标二元组i,j (l<=i<j<=r)的贡献之和。 #### 引用[.reference_title] - *1* *3* [P3722 [AH2017/HNOI2017]影魔(树状数组)](https://blog.csdn.net/li_wen_zhuo/article/details/115446022)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [洛谷3722 AH2017/HNOI2017 影魔 线段树 单调栈](https://blog.csdn.net/forever_shi/article/details/119649910)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值