版权声明:转载标注来源喔~ https://blog.csdn.net/iroy33/article/details/89963715
题意:给你n个字符串,要求找到一个最小的串,这n个字符串都是它的子串
思路:定义dp[j][s]代表以i为开头,状态为j的字符串最小长度,dp[j][s|(1<<j)]=min(dp[j][s|(1<<j)],dp[i][s]+cost[j][i])
cost[j][i]是把字符串j拼在字符串i前面使得总长度增加的长度,只需要考虑j的末尾和i的开头有多少相同
然后代码其实不难写,毕竟很暴力。但是我在计算cost的时候死了,因为我没有考虑公共长度为0。。。
关于输出:输出的时候我也死了,我记录了下一个是哪个字符串,自己写的样例都过了,呵呵哒嘤嘤嘤,明白原因了!我记录的是s[i]是最小的,但是输出的时候我只要输出s[idx].size()-cost[idx][a]开始的,这个不一定是最小的!
注意每个样例之后要多输出一行
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
const int INF=0x3f3f3f3f;
int dp[18][1<<18]; //dp[i][j]以i为最前面一个,状态为j的最小长度
int next[18][1<<18]; //开头好求,已经能知道是谁作为开头最小了
int cost[18][18]; //将i接在j之前需要增加的长度
string s[18];
int n;
//int ans=INF;
string ans;
int idx=0;
void pre()
{
for(int i=0;i<n;++i)
for(int j=0;j<n;++j)
if(s[i].find(s[j])!=string::npos)
s[j]=s[i];
sort(s,s+n);
n=unique(s,s+n)-s;
memset(cost,0,sizeof(cost));
for(int i=0;i<n;++i)
for(int j=0;j<n;++j)
{
if(i==j) continue;
int len_i=s[i].size(),len_j=s[j].size();
int len=min(len_i,len_j);
for(int k=0;k<=len;++k)
{
// cout<<l<<' '<<s[i].substr(len_i-l,l)<<' '<<s[j].substr(0,l)<<endl;
if(s[i].substr(len_i-k)==s[j].substr(0,k))
cost[i][j]=len_i-k;
}
}
}
void dfs(int id, int state)
{
if(state == 0) return;
string tmp; int next_id = -1;
for(int i = 0; i < n; i++) if(i != id && (state >> i & 1)) { //不是开头并且state里面有
if(dp[id][state] == dp[i][state & ~(1 << id)] + cost[id][i]) {
string t = s[i].substr(s[id].length() - cost[id][i], s[i].length());
if(next_id == -1 || t < tmp) { //意思是找到的不一定是字典序最小的emm
tmp = t;
next_id = i;
}
}
}
ans += tmp;
dfs(next_id, state & ~(1 << id));
}
void solve()
{
memset(dp,INF,sizeof(dp));
for(int i=0;i<n;++i)
{
dp[i][1<<i]=s[i].size();
next[i][1<<i]=-1;
}
int tot=(1<<n)-1;
for(int s=0;s<=tot;++s)
{
for(int i=0;i<n;++i)
{
if(dp[i][s]!=INF) //i最前面
{
for(int j=0;j<n;++j)
{
if(i!=j&&!(s&(1<<j)))
{
if(dp[j][s|(1<<j)]>dp[i][s]+cost[j][i]) //由于i按字典序排序了,所以是>
{
dp[j][s|(1<<j)]=dp[i][s]+cost[j][i];
next[j][s|(1<<j)]=i; //为什么这种操作不行!啊,因为我i和j重复部分是没有输出的!
}
}
}
}
}
}
int sum=INF;
idx=0;
for(int i=0;i<n;++i)
{
//c//out<<dp[i][1<<(n-1)]<<endl; dp[0][1<<(n-1)]没更新上,并且dp[1][xxx]没有加上。。
if(sum>dp[i][(1<<n)-1])
{
sum=dp[i][(1<<n)-1];
idx=i;
}
}
ans=s[idx];
// cout<<ans<<endl;
// cout<<s[idx]<<endl;
}
//这个不对。。。┭┮﹏┭┮
//void output(int idx,int state)
//{
// cout<<s[idx];
// int a=next[idx][state];
// while(a!=-1)
// {
// for(int i=s[idx].size()-cost[idx][a];i<s[a].size();++i)
// cout<<s[a][i];
// state=state&~(1<<idx);
// idx=a;
// a=next[a][state];
// }
// cout<<endl;
//}
int main()
{
int t,kase=0;
cin>>t;
while(t--)
{
cin>>n;
for(int i=0;i<n;++i)
cin>>s[i];
pre();
// for(int i=0;i<n;++i)
// {
// for(int j=0;j<n;++j)
// cout<<cost[i][j]<<' ';
// cout<<endl;
// }
solve();
cout<<"Scenario #"<<++kase<<":"<<endl;
// output(idx,(1<<n)-1);
dfs(idx,(1<<n)-1);
cout<<ans<<endl;
cout<<endl;
}
}