BZOJ-1195: [HNOI2006]最短母串

[文章目录]

Description

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

KMP。
KMP将n个字符串中作为别的字符串的子串的剔除。那么发现答案只能是原n个串的一个全排列,将其顺次首尾相接去掉公共前缀后缀所构成的串。
KMP预处理任意两个串的最长公共前后缀的长度然后状压DP即可。
对于第二关键字最小字典序,记录DP路径path,在长度相同时比较即可。
复杂度O(2^n*n^2*len)
好像AC自动机也能做且更好。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int inf=0x3f3f3f3f;
int n,mp[15][15],z1[15],z2[15],dp[1<<13][13],path[1<<13][13];
bool vis[15];
char s[15][60],st1[700],st2[700];
inline int getlen(char *x,char *y)
{
    static int nxt[60];
    int i=0,k=-1,len=strlen(x); nxt[0]=-1;
    while(i<len)
    {
        if(k==-1||x[i]==x[k]) nxt[++i]=++k;
        else k=nxt[k];
    }
    i=k=0; int lim=strlen(y);
    while(i<len&&k<lim)
    {
        if(i==-1||x[i]==y[k]) i++,k++;
        else i=nxt[i];
        if(i==len) return len;
    }
    return i;
}
inline bool cmp(char *x,char *y)
{
    int len=min(strlen(x),strlen(y));
    for(int i=0;i<len;++i)
        if(x[i]!=y[i]) return x[i]<y[i];
    return strlen(x)<strlen(y);
}
inline void output(int z[],int tail,char *st)
{
    int Len=0; z[tail+1]=0;
    for(int i=tail;i>0;--i)
    {
        for(int j=mp[z[i]][z[i+1]];j<(int)strlen(s[z[i]]);++j)
            st[Len++]=s[z[i]][j];
    }
    st[Len++]='\0';
}
inline bool find(int S,int x,int y)
{
    int t1=0,t2=0,tmp=S;
    while(x!=-1)
    {
        z1[++t1]=x+1;
        tmp-=(1<<x);
        x=path[tmp+(1<<x)][x];
    }
    tmp=S;
    while(y!=-1)
    {
        z2[++t2]=y+1;
        tmp-=(1<<y);
        y=path[tmp+(1<<y)][y];
    }
    output(z1,t1,st1); output(z2,t2,st2);
    return cmp(st1,st2);
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;++i) scanf("%s",s[i]);
    for(int i=1;i<=n;++i)
        for(int j=i+1;j<=n;++j)
        {
            if(strlen(s[i])<strlen(s[j])&&getlen(s[i],s[j])==(int)strlen(s[i])) vis[i]=1; 
            else if(getlen(s[j],s[i])==(int)strlen(s[j])) vis[j]=1;
        }
    int m=0; for(int i=1;i<=n;++i) if(!vis[i]) memcpy(s[++m],s[i],sizeof(s[i]));
    n=m;
    for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) mp[i][j]=getlen(s[i],s[j]); 
    static int a[15],b[15],len[15];
    for(int i=1;i<=n;++i) len[i]=strlen(s[i]);
    memset(dp,0x3f,sizeof dp);
    for(int i=1;i<=n;++i) dp[1<<(i-1)][i-1]=len[i],path[1<<(i-1)][i-1]=-1;
    int s1,s2;
    for(int S=1;S<(1<<n);++S)
    {
        a[0]=b[0]=0;
        for(int i=0;i<n;++i)
        {
            if((S>>i)&1) a[++a[0]]=i;
            else b[++b[0]]=i;
        }
        for(int i=1;i<=a[0];++i)
            for(int j=1;j<=b[0];++j)
            {
                s1=a[i]; s2=b[j];
                m=dp[S][s1]+len[s2+1]-mp[s2+1][s1+1];
                if( m < dp[S|(1<<s2)][s2]
                    || (m==dp[S|(1<<s2)][s2] && find(S,s1,path[S|(1<<s2)][s2]) ) )
                    dp[S|(1<<s2)][s2]=m,path[S|(1<<s2)][s2]=s1;
            }
    }
    m=(1<<n)-1; int ans1=inf,ans=0;
    for(int i=0;i<n;++i)
        if(dp[m][i]<ans1||(dp[m][i]==ans1&&find(m,i,ans)))
            ans1=dp[m][i],ans=i;
    a[0]=0;
    while(ans!=-1)
    {
        a[++a[0]]=ans+1;
        ans1=ans;
        ans=path[m][ans];
        m-=(1<<ans1);
    }
    output(a,a[0],st1); printf("%s",st1);
    return 0;
}

发表评论

邮箱地址不会被公开。 必填项已用*标注