这一题,开始自己想的时候,是想要先处理下字符串,判断下两两合成一个字符串的时候的最小字符串,但是这样处理了之后就没有头绪了,最后参考了别人的思路。
别人的思路如下:
1.首先判断一下是否有字符串是其他字符串的字串,若有则取掉。
2.计算将a与b合成一个字符串且b在前的时候,需要在a之前添加字母的最少个数increase[a][b] 比如 a = “abcdef” b=“mnbabc” 则increase[a][b] = 3
3.用dp求出 包含状态statu中的字符串且最前面一个字符串为i时的字符串的最短长度dp[i][statu],状态转移方程很容易就得到了从0向上递推就可以了
4.用深搜对dp进行搜索,搜出所要求的字符串。
AC代码如下:
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
#define MAX 0x3f3f3f3f
int N;
int increase[20][20];
int dp[15][1<<15];//dp[i][statu] 状态为statu且最前面一个数str[i]时字符串所包含字母的最少个数
string str[15];
bool substring( const string &a, const string & b ){//判断 a 是不是 b 的字串
int sz = max<int>( 0, b.size() - a.size() );
for( int i = 0; i < sz; i++ ){
int cur = i, j;
for( j = 0; j < a.size() && cur < b.size() && a[j] == b[cur]; j++, cur++ );
if( j == a.size() ){
return true;
}
}
return false;
}
int calc( const string &a, const string &b ){//计算将a与b合成一个串且b在a前时,最少需要在a之前加的字母的个数
int ans = b.size();
for( int i = 0; i < b.size(); i++ ){
int temp = i;
for( int j = 0; temp < b.size() && j < a.size() && b[temp] == a[j]; temp++, j++ );
if( temp == b.size() ){
ans = min( i, ans );
}
}
return ans;
}
void printstr( int pos, int stuts ){//用深搜输出字符串
if(stuts == (stuts&-stuts)) {
cout << str[pos];
return;
}
int ans = -1;
string s = "Z";
for( int i = 0; i < N; i++ )if( i != pos && ( stuts & ( 1 << i ) ) ){
if( dp[pos][stuts] == dp[i][stuts^(1<<pos)] + increase[i][pos] ){
string temp = str[i].substr( str[pos].size() - increase[i][pos], str[i].size() );
if( temp < s ){//按字典序输出
s = temp;
ans = i;
}
}
}
cout << str[pos].substr( 0, increase[ans][pos] );
printstr( ans, stuts ^ ( 1 << pos ) );
}
int main(){
int T, Case = 1;
cin >> T;
while( T-- ){
cin >> N;
for( int i = 0; i < N; i++ ){
cin >> str[i];
}
int temp = 0;
for( int i = 0; i < N; i++ ){
int j;
for( j = 0; j < N; j++ ){
if( i != j && substring( str[i], str[j] ) ){
break;
}
}
if( j == N )str[temp++] = str[i];
}
N = temp;
for( int i = 0; i < N; i++ ){
for( int j = 0; j < N; j++ ){
increase[i][j] = calc( str[i], str[j] );
}
}
for( int i = 0; i < N; i++ ){
for( int j = 0; j < ( 1 << N ); j++ ){
dp[i][j] = -1;
}
}
for( int i = 0; i < N; i++ ){
dp[i][1<<i] = str[i].size();
}
for( int i = 0; i < ( 1 << N ); i++ ){
for( int j = 0; j < N; j++ )if( i & ( 1 << j ) ){
for( int k = 0; k < N; k++ )if( !( ( 1 << k ) & i ) ){
int temp = dp[j][i] + increase[j][k];
if( dp[k][i|(1<<k)] == -1 || temp < dp[k][i|(1<<k)] ){
dp[k][i|(1<<k)] = temp;
}
}
}
}
int pos = 0, full = ( 1 << N ) - 1;
for( int i = 0; i < N; i++ ){
if( dp[i][full] < dp[pos][full] ){
pos = i;
}else if( dp[i][full] == dp[pos][full] ){
if( str[i] < str[pos] ){
pos = i;
}
}
}
cout << "Case " << Case++ << ": ";
printstr( pos, full );
cout << endl;
}
return 0;
}