题目链接:http://www.lightoj.com/volume_showproblem.php?problem=1073
题意:给出若干个串,求一个串s使得给出的所有串都是s的子串。要求s最短;若有多个,要求字典序最小。
思路:首先,串a包含串b,则串b无需考虑。然后预处理c[i][j],表示串i有一个长为c[i][j]的后缀同时也是串j的前缀且c[i][j]不能再长了。然后记忆化搜索即可。。。一开始我以为substring是公共子列呢。。。。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
struct node
{
char s[105];
int len,flag;
};
const int INF=1000000000;
int C,num=0;
node a[20];
int n,f[20][(1<<16)+5],c[20][20];
int next[20][105];
void initKMP(char s[],int len,int next[])
{
next[0]=-1;
int i=0,j=-1;
while(i<len)
{
if(j==-1||s[i]==s[j]) next[++i]=++j;
else j=next[j];
}
}
//判断串s1中是否包含串s2
//包含返回s2在s1的开始位置
//不包含返回-1
//next是s2的next数组
int iscontained(char *s1,int len1,char *s2,int len2,int *next)
{
int i=0,j=0;
while(i<len1)
{
if(j==-1||s1[i]==s2[j]) i++,j++;
else j=next[j];
if(j==len2) return i-len2;
}
return -1;
}
int cmp(node a,node b)
{
if(a.flag!=b.flag) return a.flag>b.flag;
return strcmp(a.s,b.s)<0;
}
int cal(node a,node b)
{
int t=min(a.len,b.len)-1,i,j,k,p;
for(i=t;i>=1;i--)
{
j=a.len-1;
k=i-1;
for(p=0;p<i;p++) if(a.s[j-p]!=b.s[k-p]) break;
if(p==i) return i;
}
return 0;
}
void init()
{
int i,j,t;
memset(c,0,sizeof(c));
for(i=0;i<n;i++) for(j=0;j<n;j++) if(i!=j&&!c[i][j])
{
t=iscontained(a[j].s,a[j].len,a[i].s,a[i].len,next[i]);
if(t==-1) continue;
a[i].flag=0;
c[i][j]=c[j][i]=1;
}
sort(a,a+n,cmp);
for(i=0;i<n&&a[i].flag;i++);
n=i;
for(i=0;i<n;i++) for(j=0;j<n;j++) if(i!=j)
c[i][j]=cal(a[i],a[j]);
}
int DFS(int cur,int st)
{
if(st==(1<<n)-1) return a[cur].len;
int &ans=f[cur][st];
if(ans!=-1) return ans;
ans=INF;
int next,st0;
for(next=0;next<n;next++) if(!(st&(1<<next)))
{
st0=st|(1<<next);
ans=min(ans,a[cur].len-c[cur][next]+DFS(next,st0));
}
return ans;
}
void print(int cur,int st)
{
if(st==(1<<n)-1)
{
printf("%s",a[cur].s);
return;
}
int i,best,ans=INF,temp,next,st0;
for(next=0;next<n;next++) if(!(st&(1<<next)))
{
st0=st|(1<<next);
temp=a[cur].len-c[cur][next]+f[next][st0];
if(temp<ans||temp==ans&&strcmp(&a[next].s[c[cur][next]],&a[best].s[c[cur][best]])<0)
{
ans=temp;
best=next;
}
}
for(i=0;i<a[cur].len-c[cur][best];i++) putchar(a[cur].s[i]);
print(best,st|(1<<best));
}
void DP()
{
memset(f,-1,sizeof(f));
int ans=INF,i,best,temp;
for(i=0;i<n;i++)
{
temp=DFS(i,1<<i);
if(temp<ans) ans=temp,best=i;
}
printf("Case %d: ",++num);
print(best,1<<best);
puts("");
}
int main()
{
for(scanf("%d",&C);C--;)
{
scanf("%d",&n);
int i;
for(i=0;i<n;i++)
{
scanf("%s",a[i].s);
a[i].len=strlen(a[i].s);
a[i].flag=1;
initKMP(a[i].s,a[i].len,next[i]);
}
init();
DP();
}
return 0;
}