2017.10.5 最短母串 思考记录

这个题n<=15,可以用10表示选取情况下的最优值

预处理两个串之间的连接关系,然后枚举状态转移

然而这个题还要输出方案,,而且还不让你开空间。

所以只能记录前继动态判断、、

所以十分难写难调



码:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n,i,j,k,l,lin[15],g1[16385][15],g2[16385][15],er[15],daan2[15],p;
long long f[16385][15],lj[16][16],ans=2147483647,len[15];
char str[15][60],daan[800],lin3[800];
bool wu[15];
int main()
{
    memset(f,0x7f,sizeof(f));
    scanf("%d",&n);
    for(i=1;i<=n;i++)
    {
    scanf("%s",str[i]+1);        
    len[i]=strlen(str[i]+1);
    }
    for(i=1;i<=n;i++)
    for(j=1;j<=n;j++)
    {
        if(i==j||len[i]>len[j])continue;
        for(k=1;k<=len[j]-len[i];k++)
        {
        for(l=1;l<=len[i];l++)
        {
      if(str[i][l]!=str[j][k+l-1])break;
        }                
        if(l>len[i])wu[i]=1;            
        }            
    }
    int cnt=0;
    for(i=1;i<=n;i++)
    {
    if(wu[i])continue;++cnt;
    for(j=1;j<=len[i];j++)
    str[cnt][j]=str[i][j];    
    len[cnt]=len[i];        
    }
    n=cnt;
    for(i=1;i<=n;i++)//最长延伸(第一个串)
    {
        for(j=1;j<=n;j++)
        {
            if(i==j)continue;
            for(k=1;k<=len[i];k++)
            {
                for(l=1;l<=len[j];l++)
                {
                if(str[i][k+l-1]!=str[j][l])break;
                }            
                if(k+l-2==len[i])
                {
                lj[i][j]=len[i]-k+1;    
                break;
                }
            }
        }        
    }
    er[0]=1;
    for(i=1;i<=n;i++)
    er[i]=er[i-1]*2;
    for(i=1;i<=n;i++)
    f[er[i-1]][i-1]=len[i],g1[er[i-1]][i-1]=-1;
    for(i=1;i<er[n];i++)//枚举状态
    {
        for(j=0;j<n;j++)//枚举上一个是啥
        {    
            for(k=0;k<n;k++)//枚举这一个放啥
        {  
            if(i&er[k])continue;
            if(f[i|er[k]][k]==f[i][j]+len[k+1]-lj[j+1][k+1]&&f[i|er[k]][k]<=1000000000)
            {
            //判断字典序;;;;;
			//if(f[i|er[]])
		int lin2=0;        
        int lol1=i,lol2=j;
        while(lol1!=-1)//拆出前继 ,放到lin里,顺序是反的 
        {
    lin[++lin2]=lol2+1;//位置要加1 
    int tmp=lol1;
    lol1=g1[lol1][lol2];
    lol2=g2[tmp][lol2];            
        }
                int lin4=0;
                lin[lin2+1]=0;
        for(l=lin2;l>=1;l--)//倒着放字符 
        {
            for(p=lj[lin[l+1]][lin[l]]+1;p<=len[lin[l]];p++)
            {
                lin3[++lin4]=str[lin[l]][p];
            }
        }
        for(l=lj[j+1][k+1]+1;l<=len[k+1];l++)
        {
        lin3[++lin4]=str[k+1][l];	        	
		}
//           for(int y=1;y<=lin4;y++)cout<<lin3[y];
//        cout<<endl;
		 lin2=0;        
         lol1=i|er[k],lol2=k;
        while(lol1!=-1)//拆出前继 ,放到daan2里,顺序是反的 
        {
    daan2[++lin2]=lol2+1;
    int tmp=lol1;
    lol1=g1[lol1][lol2];
    lol2=g2[tmp][lol2];            
        }
        //看答案是否字典序
         lin4=0;
         daan2[lin2+1]=0;
        for(l=lin2;l>=1;l--)
        {
            for(p=lj[daan2[l+1]][daan2[l]]+1;p<=len[daan2[l]];p++)
            {
                daan[++lin4]=str[daan2[l]][p];
            }
        }
//        for(int y=1;y<=lin4;y++)cout<<daan[y];
//        cout<<endl<<endl;
       bool huan=0;
	   for(l=1;l<=f[i|er[k]][k];l++)
	   {
	   	if(lin3[l]<daan[l]){huan=1;break;}
		   if(lin3[l]>daan[l]){break;   }   	
	   }
	   if(huan)
	   {
	   	 g1[i|er[k]][k]=i;
                g2[i|er[k]][k]=j;
	   }	
			}			
            if(f[i|er[k]][k]>f[i][j]+len[k+1]-lj[j+1][k+1])
            {
                f[i|er[k]][k]=f[i][j]+len[k+1]-lj[j+1][k+1];
                g1[i|er[k]][k]=i;
                g2[i|er[k]][k]=j;                
            }
        }        
        }                    
    }
    for(i=0;i<n;i++)
    ans=min(ans,f[er[n]-1][i]);
    for(i=1;i<=ans;i++)daan[i]='a';
    for(i=0;i<n;i++)
    {
    if(f[er[n]-1][i]==ans)
    {
        int lin2=n;
        int lol1=er[n]-1,lol2=i;
        while(lol1!=-1)
        {
    lin[lin2]=lol2+1;
    int tmp=lol1;
    lol1=g1[lol1][lol2];
    lol2=g2[tmp][lol2];            
        lin2--;
        }
        //看答案是否字典序
        int lin4=0;
        for(j=1;j<=n;j++)
        {
            for(k=lj[lin[j-1]][lin[j]]+1;k<=len[lin[j]];k++)
            {
                lin3[++lin4]=str[lin[j]][k];
            }
        }
        bool huan=0;
        for(j=1;j<=ans;j++)
        {
            if(daan[j]>lin3[j])
            {
                huan=1;              
                break;
            }    
            if(daan[j]<lin3[j])
            break;
        } if(huan)for(k=1;k<=ans;k++)
                daan[k]=lin3[k];
    }    
    }
    for(i=1;i<=ans;i++)
    printf("%c",daan[i]);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值