【bzoj2085】[Poi2010]Hamsters hash+倍增

dis[i][j][k]表示从第i个串的结尾到第j个串的结尾走过2^k个串的最小长度

dis[i][j][0]=len[j]-cal(i,j)
cal(i,j)表示如果i的最长后缀等于j的最长前缀

倍增floyd好了。


#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<iostream>
#define inf 100000000000000000ll
#define maxn 210

using namespace std;

unsigned long long p1=233,p2=10007;
unsigned long long hash1[210][10010],hash2[210][10010],P1[10100],P2[10010];
long long f[40][maxn][maxn],g[maxn][maxn],g1[maxn][maxn];
char s[maxn][10100];
int len[maxn];
long long ans;
int m,n;

unsigned long long Hash1(int x,int l,int r)
{
	return hash1[x][r]-hash1[x][l-1]*P1[r-l+1];
}

unsigned long long Hash2(int x,int l,int r)
{
	return hash2[x][r]-hash2[x][l-1]*P2[r-l+1];
}

int cal(int x,int y)
{
	for (int i=min(len[x],len[y])-1;i>=0;i--)
	  if (Hash1(x,len[x]-i+1,len[x])==Hash1(y,1,i) && Hash2(x,len[x]-i+1,len[x])==Hash2(y,1,i)) return i;
}

int Log(int x)
{
	int ans=0;
	while (x) x>>=1,ans++;
	return ans;
}

int main()
{
	//freopen("username.in","r",stdin);
	//freopen("username.out","w",stdout);
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++) scanf("%s",s[i]+1);
	P1[0]=1;P2[0]=1;
	for (int i=1;i<=1000;i++) P1[i]=P1[i-1]*p1,P2[i]=P2[i-1]*p2;
	for (int i=1;i<=n;i++)
	{
		len[i]=strlen(s[i]+1);
		hash1[i][0]=0;hash2[i][0]=0;
		for (int j=1;j<=len[i];j++) hash1[i][j]=hash1[i][j-1]*p1+s[i][j]-'a',hash2[i][j]=hash2[i][j-1]*p2+s[i][j]-'a';
	}
	int cnt=Log(m);
	for (int k=0;k<=cnt;k++)
	  for (int i=0;i<=n;i++)
	    for (int j=0;j<=n;j++)
	      f[k][i][j]=inf;
	for (int i=1;i<=n;i++) f[0][0][i]=len[i],f[0][i][0]=inf;
	for (int i=1;i<=n;i++)
	  for (int j=1;j<=n;j++)
	    f[0][i][j]=len[j]-cal(i,j);
	for (int k=1;k<=cnt;k++)
	  for (int p=0;p<=n;p++)
	    for (int i=0;i<=n;i++)
	      if (f[k-1][i][p]!=inf)
	        for (int j=0;j<=n;j++)
	          if (f[k-1][p][j]!=inf)
	            f[k][i][j]=min(f[k][i][j],f[k-1][i][p]+f[k-1][p][j]);
	bool flag=0;
	for (int k=0;k<=cnt;k++)
	  if (m&(1<<k))
	  {
	  	if (flag)
	  	{
	  		for (int i=0;i<=n;i++)
	  		  for (int j=0;j<=n;j++)
	  		    g1[i][j]=inf;
	  		for (int p=0;p<=n;p++)
	  		  for (int i=0;i<=n;i++)
	  		    if (g[i][p]!=inf)
	  		      for (int j=0;j<=n;j++)
	  		        if (f[k][p][j]!=inf)
	  		          g1[i][j]=min(g1[i][j],g[i][p]+f[k][p][j]);
	  		for (int i=0;i<=n;i++)
	  		  for (int j=0;j<=n;j++)
	  		    g[i][j]=g1[i][j];
	  	}
	  	else
	  	{
	  		for (int i=0;i<=n;i++)
	  		  for (int j=0;j<=n;j++)
	  		    g[i][j]=f[k][i][j];
	  		flag=1;
	  	}
	  }
	long long ans=inf;
	for (int i=1;i<=n;i++) ans=min(ans,g[0][i]);
	printf("%lld\n",ans);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值