Bzoj1195 [HNOI2006]最短母串 [状态压缩]

Time Limit: 10 Sec  Memory Limit: 32 MB
Submit: 1304  Solved: 439

Description

给定n个字符串(S1,S2,„,Sn),要求找到一个最短的字符串T,使得这n个字符串(S1,S2,„,Sn)都是T的子串。

Input

第一行是一个正整数n(n<=12),表示给定的字符串的个数。以下的n行,每行有一个全由大写字母组成的字符串。每个字符串的长度不超过50.

Output

只有一行,为找到的最短的字符串T。在保证最短的前提下,如果有多个字符串都满足要求,那么必须输出按字典序排列的第一个。

Sample Input

2
ABCD
BCDABC

Sample Output

ABCDABC

HINT

 

Source

AC自动机+BFS:http://www.cnblogs.com/SilverNebula/p/6445516.html

状压DP+神(bao)奇(li)预处理

先找出所有被其他串包含的串并扔掉(显然)

暴力预处理出每两个串连接起来,公共部分的长度。

进行状压DP,暴力储存每个状态对应的字符串。

在所有的11111...的最终状态中,暴力找出字典序最小的那个

是不是很神(bao)奇(li)?

不知道为何,用strcmp会TLE(也可能是别的处理不到位),各处优化了一下,手写了比较函数,终于过了

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 #include<queue>
 6 using namespace std;
 7 const int INF=0x3f3f3f3f;
 8 const int mxn=61;
 9 int n;
10 struct bind{
11     char s[605];
12     int len;
13     bool operator < (const bind y) const {
14         if(len!=y.len)return len<y.len;
15         for(int i=0;i<len;i++)
16             if(s[i]!=y.s[i])return s[i]<y.s[i];
17         return 0;
18     }
19 }f[1<<12][12],s[12];
20 int c[13][13];
21 bool ban[mxn];
22 
23 bool ovl(int i,int j){//判断能否覆盖 
24     if(s[i].len<s[j].len)return 0;
25     char *p=strstr(s[i].s,s[j].s);
26     if(p==NULL)return 0;
27     return 1;
28 }
29 int clc(int x,int y){//统计共用串长度 
30     bool flag=0;
31     for(int i=max(0,s[x].len-s[y].len);i<s[x].len;i++){
32         flag=1;
33         for(int j=i;j<s[x].len;j++)
34             if(s[x].s[j]!=s[y].s[j-i]){flag=0;break;}
35         if(flag)return s[x].len-i;//--1
36     }
37     return 0;
38 }
39 bind merge(int S,int u,int v){//字符串合并 
40     bind tmp=f[S][u];
41     strcat(tmp.s,s[v].s+c[u][v]);
42     tmp.len=f[S][u].len-c[u][v]+s[v].len;
43 //    printf("merge:%d %d %d :%d
",S,u,v,tmp.len);
44     return tmp;
45 }
46 void Dp(){
47     int i,j,ed=(1<<n)-1;
48     for(i=0;i<=ed;i++)
49         for(j=0;j<n;j++)f[i][j].len=INF;//init
50     for(i=0;i<n;i++)f[1<<i][i]=s[i];
51     for(i=1;i<=ed;i++){
52         for(j=0;j<n;j++){
53             if((i>>j)&1)
54                 for(int k=0;k<n;k++){
55                     if((i>>k)&1)continue;
56                     bind tmp=merge(i,j,k);
57                     if(tmp<f[i|(1<<k)][k])f[i|(1<<k)][k]=tmp;
58                 }
59         }
60     }
61 }
62 int main(){
63     int i,j;
64     scanf("%d",&n);
65     for(i=0;i<n;i++)scanf("%s",s[i].s),s[i].len=strlen(s[i].s);
66     for(i=0;i<n;i++)
67         for(j=0;j<n;j++)
68             if(i!=j && ovl(i,j) && !ban[i])ban[j]=1;
69     int cnt=0;
70     for(i=0;i<n;i++)if(!ban[i])s[cnt++]=s[i];
71     n=cnt;
72     for(i=0;i<n;i++)
73         for(j=0;j<n;j++)
74             if(i!=j)c[i][j]=clc(i,j);
75     Dp();
76     int ans=0,ed=(1<<n)-1;
77     for(i=1;i<n;i++)
78         if(f[ed][i]<f[ed][ans])ans=i;
79     printf("%s",f[ed][ans].s);
80     return 0;
81 }