bzoj1195: [HNOI2006]最短母串

18 篇文章 0 订阅

一开始以为这是字符串好题,一看n范围,n!就炸了。

考虑dp,dp[i][j] i表示每个子串有没有出现,j表示最后的串。

看了lych的博客之后,自己还是naive!首先有可能出现一个串没有用,要预处理掉,另外字典序最小需要存更多的东西。复杂度O(2^n*n*n*跑得过)。

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 13

using namespace std;
int n,m,l[N],fl[N],pre[N][N],dp[4096][N],b[N];
char s[N][51],c[4096][13][601];
bool same(int x,int a,int y,int b,int c)
{
	for (int i=0;i<c;i++)
		if (s[x][a+i]!=s[y][b+i]) return 0;
	return 1;
}
int main()
{
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
	{
		scanf("%s",s[i]+1);
		l[i]=strlen(s[i]+1);
	}
	//去重
	for (int i=1;i<=n;i++)
		for (int j=n+1;j<=n;j++)
			if (l[i]>=l[j])
			{
				for (int k=1;k<=l[i]-l[j]+1;k++)
					if (same(i,k,j,1,l[j]))fl[j]=1;
			}
			else
			{
				for (int k=1;k<=l[j]-l[i]+1;k++)
					if (same(i,1,j,k,l[i]))fl[i]=1;
			}
	for (int i=1;i<=n;i++)
		if (!fl[i])
		{
			l[++m]=l[i];
			for (int j=1;j<=l[m];j++)
				s[m][j]=s[i][j];
		}
	n=m;
	//pre
	for (int i=1;i<=n;i++)
		for (int j=1;j<=n;j++)
			if (i!=j)
			{
				int tmp=l[i]<l[j]?1:l[i]-l[j]+1;
			
				for (;tmp<=l[i];tmp++)
					if (same(i,tmp,j,1,l[i]-tmp+1)) break;
				pre[i][j]=l[j]-l[i]+tmp-1;
			}
	//dp[i][j] i:状态 j:最后的串
	b[1]=1;
	for (int i=2;i<=n+1;i++)
		b[i]=b[i-1]<<1;
	memset(dp,0x3f,sizeof dp);
	for (int i=1;i<=n;i++)
	{
		dp[b[i]][i]=l[i];
		for (int j=1;j<=l[i];j++)
			c[b[i]][i][j]=s[i][j];
	}
	for (int i=1;i<=b[n+1]-2;i++)
	{
		for (int j=1;j<=n;j++)
		{
			//cout<<dp[i][j]<<endl;
			if (dp[i][j]>100000) continue;
			for (int k=1;k<=n;k++)
				if ((i&b[k])==0)
				{
					//cout<<i<<j<<k<<endl;
					if (dp[i][j]+pre[j][k]<dp[i|b[k]][k])
					{
						dp[i|b[k]][k]=dp[i][j]+pre[j][k];
						for (int p=1;p<=dp[i][j];p++)
							c[i|b[k]][k][p]=c[i][j][p];
						for (int p=1;p<=pre[j][k];p++)
							c[i|b[k]][k][dp[i][j]+p]=s[k][l[k]-pre[j][k]+p];
					}
					else
					{
						if (dp[i][j]+pre[j][k]==dp[i|b[k]][k])
						{
							int fl=0;
							for (int p=1;;p<=dp[i][j],p++)
							{
								if (c[i|b[k]][k][p]<c[i][j][p])
									{fl=1;break;}
								if (c[i|b[k]][k][p]>c[i][j][p])
									{fl=2;break;}
							}
							if (fl==1) continue;
							if (fl==2)
							{
								for (int p=1;p<=dp[i][j];p++)
									c[i|b[k]][k][p]=c[i][j][p];
								for (int p=1;p<=pre[j][k];p++)
									c[i|b[k]][k][dp[i][j]+p]=s[k][l[k]-pre[j][k]+p];
								continue;
							}
							for (int p=1;p<=pre[j][k];p++)
							{
								if (c[i|b[k]][k][pre[j][k]+p]<s[k][l[k]-pre[j][k]+p])
									{fl=1;break;}
								if (c[i|b[k]][k][pre[j][k]+p]>s[k][l[k]-pre[j][k]+p])
									{fl=2;break;}
							}
							if (fl==1) continue;
							if (fl==2)
							{
								for (int p=1;p<=pre[j][k];p++)
									c[i|b[k]][k][dp[i][j]+p]=s[k][l[k]-pre[j][k]+p];
								continue;
							}
						}
					}
				}
		}
	}
	int Ans=1;
	for (int i=2;i<=n;i++)
	{
		if (dp[b[n+1]-1][i]<dp[b[n+1]-1][Ans]) Ans=i;
		else if (dp[b[n+1]-1][i]==dp[b[n+1]-1][Ans])
		{
			int fl=0;
			for (int p=1;p<=dp[b[n+1]-1][i];p++)
			{
				if (c[b[n+1]-1][i][p]<c[b[n+1]-1][Ans][p])
					{fl=2;break;}
				if (c[b[n+1]-1][i][p]>c[b[n+1]-1][Ans][p])
					{fl=1;break;}
			}
			if (fl==2)
				Ans=i;
		}
	}
	for (int i=1;i<=dp[b[n+1]-1][Ans];i++)
		putchar(c[b[n+1]-1][Ans][i]);
	//while(1);
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值