1195: [HNOI2006]最短母串
Time Limit: 10 Sec Memory Limit: 32 MBSubmit: 1212 Solved: 405
[ Submit][ Status][ Discuss]
Description
给定n个字符串(S1,S2,„,Sn),要求找到一个最短的字符串T,使得这n个字符串(S1,S2,„,Sn)都是T的子串。
Input
第一行是一个正整数n(n<=12),表示给定的字符串的个数。以下的n行,每行有一个全由大写字母组成的字符串。每个字符串的长度不超过50.
Output
只有一行,为找到的最短的字符串T。在保证最短的前提下,如果有多个字符串都满足要求,那么必须输出按字典序排列的第一个。
Sample Input
2
ABCD
BCDABC
ABCD
BCDABC
Sample Output
ABCDABC
HINT
Source
题解:状压dp
将答案中是否包含字符串i,状压起来。
f[i][j]表示到状态i,最前面的字符串为j 的最小连接方式的下一个串的编号
g[i][j] 表示状态i,最前面的字符串为j此时的长度
h[i][j]记录从哪个状态推来的。
转移的时候需要根据记录的后继还原出字符串进行对比
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#define N 15
#define pa pair<int,int>
using namespace std;
int f[(1<<12)][13],g[(1<<12)][13],h[(1<<12)][13],can[(1<<12)][13];
int n,m,ans,c[N][N];
char a[N][100],b[N][100],str[2000],s[603],s1[603];
int len[N],len1[N],mark[N];
int calc(char a[],char b[],int len,int len1)
{
int ans=0;
for (int i=1;i<=len;i++)
{
int j=1; int k=i;
while (a[k]==b[j]&&k<=len&&j<=len1) k++,j++;
if (k==len+1) ans=max(ans,j-1);
}
return ans;
}
bool pd(char x[],char y[],int len)
{
for (int i=1;i<=len;i++)
if (x[i]<y[i]) return false;
else if (x[i]>y[i]) return true;
return false;
}
int change(int x,int sta,int lenk,int last,char s[])
{
int t;
if (last==-1) t=calc(s,a[x],lenk,len[x]);
else t=c[last][x];
for (int i=t+1;i<=len[x];i++)
s[++lenk]=a[x][i];
if (f[sta][x]==0) return lenk;
change(f[sta][x],h[sta][x],lenk,x,s);
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
scanf("%s",b[i]+1);
len1[i]=strlen(b[i]+1);
}
for (int i=1;i<=n;i++)
if (!mark[i])
for (int j=1;j<=len1[i];j++)
{
for (int k=1;k<=n;k++)
if (k!=i)
{
bool f=true;
for (int l=1;l<=len1[k];l++)
if (b[i][j+l-1]!=b[k][l]){
f=false;
break;
}
if (f&&j+len1[k]-1<=len1[i])
mark[k]=1;
}
}
int cnt=0;
for (int i=1;i<=n;i++)
if (!mark[i]) {
cnt++; len[cnt]=len1[i];
for (int j=1;j<=len[cnt];j++)
a[cnt][j]=b[i][j];
}
n=cnt;
for (int i=1;i<=n;i++) c[0][i]=0;
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
if (i!=j) c[i][j]=calc(a[i],a[j],len[i],len[j]);
memset(f,-1,sizeof(f));
queue<pa> p;
for (int i=1;i<=n;i++)
f[1<<(i-1)][i]=0,g[1<<(i-1)][i]=len[i],can[1<<(i-1)][i]=1,
p.push(make_pair(1<<(i-1),i));
while (!p.empty())
{
pa x=p.front(); p.pop();
int i=x.second; int sta=x.first; can[sta][i]=0;
for (int j=1;j<=n;j++)
if (!((sta>>(j-1))&1))
{
for (int k=1;k<=len[j];k++) s1[k]=a[j][k];
int t=change(i,sta,len[j],-1,s1);
if (f[sta|(1<<(j-1))][j]==-1)
{
f[sta|(1<<(j-1))][j]=i; h[sta|(1<<(j-1))][j]=sta;
g[sta|(1<<(j-1))][j]=t;
if (!can[sta|(1<<(j-1))][j]) p.push(make_pair(sta|(1<<(j-1)),j));
continue;
}
int l=change(j,sta|(1<<(j-1)),0,0,s);
if (t<l)
{
f[sta|(1<<(j-1))][j]=i; h[sta|(1<<(j-1))][j]=sta;
g[sta|(1<<(j-1))][j]=t;
if (!can[sta|(1<<(j-1))][j]) p.push(make_pair(sta|(1<<(j-1)),j));
}
else
if (t==l)
if (pd(s,s1,l))
{
f[sta|(1<<(j-1))][j]=i,h[sta|(1<<(j-1))][j]=sta;
if (!can[sta|(1<<(j-1))][j]) p.push(make_pair(sta|(1<<(j-1)),j));
}
}
}
int t=(1<<n)-1; ans=2000;
for (int i=1;i<=n;i++)
if (f[t][i]!=-1) ans=min(ans,g[t][i]);
for (int i=1;i<=600;i++)
str[i]='Z';
for (int i=1;i<=n;i++)
if (g[t][i]==ans)
{
int l=change(i,t,0,0,s);
if (pd(str,s,ans))
for (int j=1;j<=ans;j++) str[j]=s[j];
}
for (int i=1;i<=ans;i++)
printf("%c",str[i]);
}