Light 1073 DNA Sequence(记忆化搜索)

题目链接: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;

 }

  

你可能感兴趣的:(sequence)