按照子串出现的先后考虑。令f[i][j]为已经出现的字符串集合为i,最后一个出现的字符串为j时的最短串长,预处理一下任意两个串的最长重叠长度,转移显然。有点麻烦的是字典序,强行增加代码难度。
另一个比较简单的做法是上AC自动机,建出来后类似地令f[i][j]为已经出现的字符串集合为i,在自动机上点j时的最短串长,相当于跑一个最短路,bfs时每次优先选字典序最小的边即可。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } #define N 12 #define M 52 int n,len[N],d[N][N]; char s[N][M]; struct str { int n;char s[N*M]; bool operator <(const str&a) const { for (int i=1;i<=n;i++) if (s[i]!=a.s[i]) return s[i]<a.s[i]; return 1; } }; str get(int p,int q); struct data { int i,j,x,n,s[N+1]; bool operator <(const data&a) const { if (x!=a.x) return x<a.x; return get(i,j)<get(a.i,a.j); } }f[1<<N][N]; str get(int p,int q) { int n=f[p][q].n; str v;memset(v.s,0,sizeof(v.s)); int x=len[f[p][q].s[1]]; for (int i=1;i<=len[f[p][q].s[1]];i++) v.s[i]=s[f[p][q].s[1]][i]; for (int i=2;i<=n;i++) for (int j=d[f[p][q].s[i-1]][f[p][q].s[i]]+1;j<=len[f[p][q].s[i]];j++) v.s[++x]=s[f[p][q].s[i]][j]; v.n=x; return v; } int main() { #ifndef ONLINE_JUDGE freopen("bzoj1195.in","r",stdin); freopen("bzoj1195.out","w",stdout); const char LL[]="%I64d "; #else const char LL[]="%lld "; #endif n=read(); for (int i=0;i<n;i++) scanf("%s",s[i]+1),len[i]=strlen(s[i]+1); for (int i=0;i<n;i++) for (int j=0;j<n;j++) if (i!=j) for (int k=1;k<=len[i];k++) { bool flag=1; for (int x=1;x<=len[j]&&k+x-1<=len[i];x++) if (s[i][k+x-1]!=s[j][x]) {flag=0;break;} if (flag) {d[i][j]=min(len[i]-k+1,len[j]);break;} } for (int i=1;i<(1<<n);i++) for (int j=0;j<n;j++) f[i][j].x=1000,f[i][j].i=i,f[i][j].j=j; for (int i=0;i<n;i++) f[1<<i][i].x=len[i],f[1<<i][i].s[1]=i,f[1<<i][i].n=1; for (int i=1;i<(1<<n);i++) for (int j=0;j<n;j++) if (i&(1<<j)) for (int k=0;k<n;k++) if (!(i&(1<<k))) if (d[j][k]==len[k]) f[i|(1<<k)][j]=min(f[i|(1<<k)][j],f[i][j]),f[i|(1<<k)][j].i=i|(1<<k); else { data t=f[i][j]; f[i][j].x+=len[k]-d[j][k]; f[i][j].s[++f[i][j].n]=k; f[i|(1<<k)][k]=min(f[i|(1<<k)][k],f[i][j]),f[i|(1<<k)][k].i=i|(1<<k),f[i|(1<<k)][k].j=k; f[i][j]=t; } for (int i=1;i<n;i++) f[(1<<n)-1][0]=min(f[(1<<n)-1][0],f[(1<<n)-1][i]),f[(1<<n)-1][0].j=0; printf("%s",get((1<<n)-1,0).s+1); return 0; }