UVA 1204

好久没写题解了,现在写一篇。

首先我们可以想到一个O(n2)DP——f(S,i,0/1)表示当前我们考虑字符串集合为S,最后一个字符串为i,是正着还是反着放的。(这类“正反”是相较于第一个字符串而言)

转移时,枚举下一个字符串,减去和i相交的部分长度即可,最后统计答案时,由于是个环,我们还要减去最后一个选择的字符串和第一个字符串的相交部分长度。

真的就这么简单吗?

有两个问题:

  1. 出现包含关系怎么办?

  2. 如果有一个或多个子串串是转了很多圈得到的怎么办?

第一个问题很好解决——对于一个字符串S,存在另一个子串T的子串T=S,那么S完全包含于T中,去掉S即可。

比如样例2,GBBBG是BGGGBBBGG的子串,那么我们就只考虑BGGGBBBGG即可。

第二个问题困扰了我很久。其实是没有关系的。

设最终答案长为L

如果A是转了a圈的,剩下a个字符(|A|=a×L+a),B是转了b圈,剩下b个字符的(|B|=b×L+b)。

如果a<b,那么A将完全包含在B中;否则如果a>bB将包含在A中;a=b时,我们减去相交的部分,剩下的长度不会超过L

所以即使|A|,|B|都大于L,我们仍然能得到正确的答案。

还有,注意特判去掉包含关系后,只剩下一个字符串的情况。我们只需枚举最终环长L,暴力检查即可。

附上冗余的代码:

#include<bits/stdc++.h>
#define debug(...) std::cerr<<#__VA_ARGS__<<" : "<<__VA_ARGS__<<std::endl
int n,share[20][2][20][2],f[1<<20][20][2];
int main() {
	while(std::cin>>n&&n) {
		memset(share,0,sizeof share);
		std::vector<std::string> str(n);
		for(auto &item : str) std::cin>>item;
		for(int i=0;i<(int)str.size();) {
			bool is=1;
			for(int j=0;j<(int)str.size();j++) {
				if(i==j||str[j].size()<str[i].size()) continue;
				for(int k=0;k<=(int)str[j].size()-(int)str[i].size();k++) {
					if(str[j].substr(k,(int)str[i].size())==str[i]) {
						is=0;
						break;
					}
				}
				if(!is) break;
			}
			if(!is) {
				str.erase(str.begin()+i);
			} else i++;
		}
	//	for(auto item : str) std::cout<<item<<'\n';
		if((int)str.size()==1) {
			std::string s=str[0];
			if(s.size()==1) printf("2\n");
			else {
				for(int leap=2;leap<=(int)s.size();leap++) {
					bool chk=1;
					for(int i=leap;i<(int)s.size();i++) {
						if(s[i]!=s[i%leap]) {
							chk=0;
							break;
						}
					}
					if(chk) {
						printf("%d\n",leap);
						break;
					}
				}
			}
			continue;
		}
		for(int i=0;i<(int)str.size();i++) for(int j=0;j<(int)str.size();j++) {
			if(i==j) continue;
			for(int p1=0;p1<2;p1++) for(int p2=0;p2<2;p2++) {
				std::string x=str[i],y=str[j];
				if(p1) std::reverse(x.begin(),x.end());
				if(p2) std::reverse(y.begin(),y.end());
				for(int k=0;k<(int)x.size();k++) {
					if(k+(int)y.size()<=(int)x.size()) continue;
					int is=1;
					for(int l=k,m=0;l<(int)x.size();l++,m++) {
						if(x[l]!=y[m]) {
							is=0;
							break;
						}
					}
					if(is) {
						share[i][p1][j][p2]=(int)x.size()-k;
						break;
					}
				}
			}
		}
		int S=1<<(int)str.size();
		for(int i=0;i<S;i++) {
			for(int j=0;j<n;j++) {
				for(int p=0;p<2;p++) {
					f[i][j][p]=1000000000;
				}
			}
		}
		f[1][0][0]=(int)str[0].size();
		for(int i=0;i<S;i++) {
			for(int j=0;j<n;j++) {
				for(int p=0;p<2;p++) {
					if(f[i][j][p]==1000000000) continue;
					for(int k=0;k<n;k++) {
						if(i>>k&1) continue;
						for(int q=0;q<2;q++) {
							f[i|(1<<k)][k][q]=std::min(f[i|(1<<k)][k][q],f[i][j][p]+(int)str[k].size()-share[j][p][k][q]);
						}
					}
				}
			}
		}
		int ans=1000000000;
		for(int j=1;j<n;j++) {
			for(int p=0;p<2;p++) {
				if(f[S-1][j][p]==1000000000) continue;
				ans=std::min(ans,f[S-1][j][p]-share[j][p][0][0]);
			}
		}
		printf("%d\n",std::max(ans,2));
	}
	return 0;
}
posted @   Nastia  阅读(32)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示