题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1195
其实我写麻烦了。。。
设dp[i][j]表示在i这个状态下,我们连接这些字符串且以第j个字符串为结尾所形成的字符串最短为少,g[i][j]储存这个状态下的字符串是什么。bin[i]=2^i;
那么转移方程就是dp[i|bin[k]][j]=min(dp[i|bin[k]][j],dp[i][j]+(把第k个字符串连到第j个字符串后面所会增加的长度));(j是我们枚举的i状态中已连接的字符串,k是i状态中还未连接的字符串)
很明显,那些连接任意两个字符串所要增加的长度可以事先处理出来。g数组的处理也差不多,只不过加的是把第k个字符串连到第j个字符串后面所会增加的字符串(除去公共部分)。
接下来说一些小细节:
1.首先,如果一个字符串是其他串的子串的话,就直接把它除去,我在程序中判断子串是用kmp的),为了练习一下,其实直接枚举就好了。
2.其次,我们要怎么判断两个字串相连的公共部分最长是多少呢,其实是O(n)扫一遍,这个程序里面详细写。
唔,其他细节程序里写。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<string> #define maxx 100000000 using namespace std; int ans,len[30][30],dp[6009][30],n,next[30],bin[30]; string a[30],b[30],g[6009][30],len2[30][30]; bool check(string&s1,string&s2) //检查s1是不是s2的子串,我用了kmp,还是建议枚举。 { int l1=s1.size(),l2=s2.size(); if (l1>l2) return false; int now=-1; next[0]=-1; for (int i=1;i<l1;i++) { while ((now!=-1)&&(s1[now+1]!=s1[i])) now=next[now]; if (s1[now+1]==s1[i]) now++; next[i]=now; } now=-1; for (int i=0;i<l2;i++) { while ((now!=-1)&&(s2[i]!=s1[now+1])) now=next[now]; if (s2[i]==s1[now+1]) now++; if (now==l1-1) return true; } return false; } void calc(int x,int y) //计算第x和第y的子串,连接他们会增加的长度,和增加的字符串 { string s1=a[x],s2=a[y]; int tot=0,l1=s1.size(),l2=s2.size(); int h1=l1-1,h2=0; string p1,p2; while ((h1>=0)&&(h2<l2)) { p1=s1[h1--]+p1; p2=p2+s2[h2++]; if (p1==p2) tot=max(tot,h2); } len[x][y]=l2-tot; for (int i=tot;i<l2;i++) len2[x][y]=len2[x][y]+s2[i]; //这个是如果连接着两个字符串的话,在第X个字符串后面会增加的字符串是多少 } int main() { scanf("%d",&n); char s[100]; for (int i=1;i<=n;i++) { scanf("%s",s); string t; for (int j=0;j<strlen(s);j++) t=t+s[j]; b[i]=t; } int cnt=0; for (int i=1;i<=n;i++) { bool fg=false; for (int j=1;j<=n;j++) if ((i!=j)&&(check(b[i],b[j]))) { fg=true; break; } if (!fg) a[++cnt]=b[i]; } //删去被别人包含的字符串 n=cnt; if (n==0) cout<<b[1]<<endl;//如果字符串全部相同的话,就不用计算了 else { for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) if (i!=j) calc(i,j); //计算两个字符串连接后会增加的部分 bin[0]=1; for (int i=1;i<=n;i++) bin[i]=bin[i-1]*2; for (int i=0;i<bin[n];i++) for (int j=1;j<=n;j++) dp[i][j]=maxx; for (int i=1;i<=n;i++) { dp[bin[i-1]][i]=a[i].size(); g[bin[i-1]][i]=a[i]; } //dp for (int i=0;i<bin[n];i++) for (int j=1;j<=n;j++) if ((i&bin[j-1])&&(dp[i][j]!=maxx)) for (int k=1;k<=n;k++) if (!(i&bin[k-1])) { if (dp[i|bin[k-1]][k]==dp[i][j]+len[j][k]) g[i|bin[k-1]][k]=min(g[i|bin[k-1]][k],g[i][j]+len2[j][k]); if (dp[i|bin[k-1]][k]>dp[i][j]+len[j][k]) { dp[i|bin[k-1]][k]=dp[i][j]+len[j][k]; g[i|bin[k-1]][k]=g[i][j]+len2[j][k]; } } ans=maxx; string ans2; //统计答案 for (int i=1;i<=n;i++) if (dp[bin[n]-1][i]<ans) { ans=dp[bin[n]-1][i]; ans2=g[bin[n]-1][i]; } else if (dp[bin[n]-1][i]==ans) ans2=min(ans2,g[bin[n]-1][i]); cout<<ans2<<endl; } return 0; }