好久没写题解了,现在写一篇。
首先我们可以想到一个\(O(n^2)\)DP——\(f(S,i,0/1)\)表示当前我们考虑字符串集合为\(S\),最后一个字符串为\(i\),是正着还是反着放的。(这类“正反”是相较于第一个字符串而言)
转移时,枚举下一个字符串,减去和\(i\)相交的部分长度即可,最后统计答案时,由于是个环,我们还要减去最后一个选择的字符串和第一个字符串的相交部分长度。
真的就这么简单吗?
有两个问题:
-
出现包含关系怎么办?
-
如果有一个或多个子串串是转了很多圈得到的怎么办?
第一个问题很好解决——对于一个字符串\(S\),存在另一个子串\(T\)的子串\(T'=S\),那么\(S\)完全包含于\(T\)中,去掉\(S\)即可。
比如样例\(2\),GBBBG是BGGGBBBGG的子串,那么我们就只考虑BGGGBBBGG即可。
第二个问题困扰了我很久。其实是没有关系的。
设最终答案长为\(L\)。
如果\(A\)是转了\(a\)圈的,剩下\(a'\)个字符(\(|A|=a\times L+a'\)),\(B\)是转了\(b\)圈,剩下\(b'\)个字符的(\(|B|=b\times L+b'\))。
如果\(a<b\),那么\(A\)将完全包含在\(B\)中;否则如果\(a>b\),\(B\)将包含在\(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;
}
标签:std,子串,包含,1204,即可,字符串,UVA,减去
From: https://www.cnblogs.com/Nastia/p/16854263.html