poj 1226 Substrings

求多个字符串的最长公共字串

利用后缀数组求解,将所有的字符串连接成一个长的字符串,二分所有字符串中最短的字符串的长度mid为公共前缀的长度,求出所有满足公共前缀长度大于等于mid的后缀,判断所有的字符串是否都包含在这些后缀中,都包含,则mid为一个可行的长度。


#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

#define maxn 200005

int wa[maxn],wb[maxn],wv[maxn],Ws[maxn];

int cmp(int *r,int a,int b,int l)
{
	return r[a]==r[b]&&r[a+l]==r[b+l];
}

void da(const int *r,int *sa,int n,int m)
{
	int i,j,p,*x=wa,*y=wb,*t;
	for(i = 0;i<m;i++)
		Ws[i] = 0;
	for(i = 0;i<n;i++)
		Ws[x[i]=r[i]]++;
	for(i = 1;i<m;i++)
		Ws[i]+=Ws[i-1];
	for(i = n-1;i>=0;i--)
		sa[--Ws[x[i]]] = i;
	for(j = 1,p = 1;p<n;j*=2,m=p)
	{
		for(p = 0,i = n-j;i<n;i++)
			y[p++] = i;
		for(i = 0;i<n;i++)
			if(sa[i]>=j)
				y[p++] = sa[i] - j;
		for(i = 0;i<n;i++)
			wv[i] = x[y[i]];
		for(i = 0;i<m;i++)
			Ws[i] = 0;
		for(i = 0;i<n;i++)
			Ws[wv[i]]++;
		for(i = 1;i<m;i++)
			Ws[i]+=Ws[i-1];
		for(i = n-1;i>=0;i--)
			sa[--Ws[wv[i]]] = y[i];
		for(t = x,x = y,y = t,p = 1,x[sa[0]]=0,i = 1;i<n;i++)
			x[sa[i]] = cmp(y,sa[i-1],sa[i],j)?p-1:p++;
	}
}

int sa[maxn],Rank[maxn],height[maxn];

void calheight(const int *r,int *sa,int n)
{
	int i,j,k = 0;
	for(i = 1;i<=n;i++)
		Rank[sa[i]] = i;
	for(i = 0;i<n;height[Rank[i++]]=k)
		for(k?k--:0,j=sa[Rank[i]-1];r[i+k]==r[j+k];k++);

}

int str[maxn];
int local[maxn];
bool flag[105];

int solve(int minlen,int n,int cnt)
{
	int s = 0,e = minlen,mid,appear,ans = 0;
	while(s<=e)
	{
		memset(flag,0,sizeof(flag));
		appear = 0;
		mid = (s+e)>>1;
		bool tmp = false;
		for(int i = 1;i<=n;i++)
		{
			if(height[i]>=mid)
			{
				int num = local[sa[i]];
				if(!flag[num])
				{	
					appear++;
					flag[num] = 1;
				}
				if(appear==cnt)
				{
					tmp = true;
					break;
				}
			}
			else
			{
				appear = 0;
				memset(flag,0,sizeof(flag));
				int num = local[sa[i]];
				appear++;
				flag[num] = 1;
			}
		}
		if(tmp)
		{
			s = mid+1;
			ans = mid;
		}
		else
		{
			e = mid-1;
		}
	}
	return ans;
}

int main()
{
	int t,n,minlen=105;
	int spe = 500;
	char s[200];
	for(scanf("%d",&t);t;t--)
	{
		scanf("%d",&n);
		minlen = 105;
		int k = 0;
		for(int i = 0;i<n;i++)
		{
			scanf("%s",s);
			int len = strlen(s);
			if(len < minlen)
				minlen = len;
			for(int j = 0;j<len;j++)
			{
				str[k] = s[j];
				local[k++] = i;
			}
			str[k] = spe++;
			local[k++] = i;
			for(int j = len-1;j>=0;j--)
			{
				str[k] = s[j];
				local[k++] = i;
			}
			str[k] = spe++;
			local[k++] = i;
		}
		str[k] = 0;
		local[k] = n;
		da(str,sa,k+1,spe);
		calheight(str,sa,k);
		int ans = solve(minlen,k,n);
		printf("%d\n",ans);
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值