UVA 1204
好久没写题解了,现在写一篇。
首先我们可以想到一个DP——表示当前我们考虑字符串集合为,最后一个字符串为,是正着还是反着放的。(这类“正反”是相较于第一个字符串而言)
转移时,枚举下一个字符串,减去和相交的部分长度即可,最后统计答案时,由于是个环,我们还要减去最后一个选择的字符串和第一个字符串的相交部分长度。
真的就这么简单吗?
有两个问题:
-
出现包含关系怎么办?
-
如果有一个或多个子串串是转了很多圈得到的怎么办?
第一个问题很好解决——对于一个字符串,存在另一个子串的子串,那么完全包含于中,去掉即可。
比如样例,GBBBG是BGGGBBBGG的子串,那么我们就只考虑BGGGBBBGG即可。
第二个问题困扰了我很久。其实是没有关系的。
设最终答案长为。
如果是转了圈的,剩下个字符(),是转了圈,剩下个字符的()。
如果,那么将完全包含在中;否则如果,将包含在中;时,我们减去相交的部分,剩下的长度不会超过。
所以即使都大于,我们仍然能得到正确的答案。
还有,注意特判去掉包含关系后,只剩下一个字符串的情况。我们只需枚举最终环长,暴力检查即可。
附上冗余的代码:
#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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话