poj 1226 Substrings (后缀数组)

本文介绍了一种利用后缀数组解决寻找多个字符串中出现或反转后出现的最长公共子串的问题。通过将字符串进行特殊处理并连接后,运用二分查找及后缀数组技术高效地找出最长公共子串。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Substrings
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 13919 Accepted: 4922

Description

You are given a number of case-sensitive strings of alphabetic characters, find the largest string X, such that either X, or its inverse can be found as a substring of any of the given strings.

Input

The first line of the input contains a single integer t (1 <= t <= 10), the number of test cases, followed by the input data for each test case. The first line of each test case contains a single integer n (1 <= n <= 100), the number of given strings, followed by n lines, each representing one string of minimum length 1 and maximum length 100. There is no extra white space before and after a string.

Output

There should be one line per test case containing the length of the largest string found.

Sample Input

2
3
ABCD
BCDFF
BRCD
2
rose
orchid

Sample Output

2
2 

Source

[Submit]   [Go Back]   [Status]   [Discuss]


题目大意:出现或反转后出现在每个字符串中的最长公共子串。

题解:后缀数组

将每个串反转后接在原串后面,再将所有串相连。然后二分判定即可。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define N 100003
using namespace std;
int m,n,p,len;
int sa[N],rank[N],height[N],xx[N],yy[N],*x,*y;
int b[N],a[N],vis[N],pos[N];
char s[N];
void init()
{
	memset(sa,0,sizeof(sa));
	memset(rank,0,sizeof(rank));
	memset(b,0,sizeof(b));
	memset(vis,0,sizeof(vis));
	memset(pos,0,sizeof(pos));
	memset(a,0,sizeof(a));
	memset(height,0,sizeof(height));
}
int cmp(int i,int j,int l)
{
	return y[i]==y[j]&&(i+l>len?-1:y[i+l])==(j+l>len?-1:y[j+l]);
}
void get_SA()
{
	x=xx; y=yy; m=500;
	for (int i=1;i<=len;i++) b[x[i]=a[i]]++;
	for (int i=1;i<=m;i++) b[i]+=b[i-1];
	for (int i=len;i>=1;i--) sa[b[x[i]]--]=i;
	for (int k=1;k<=len;k<<=1) {
		p=0;
		for (int i=len-k+1;i<=len;i++) y[++p]=i;
		for (int i=1;i<=len;i++)
		 if (sa[i]>k) y[++p]=sa[i]-k;
		for (int i=1;i<=m;i++) b[i]=0;
		for (int i=1;i<=len;i++) b[x[y[i]]]++;
		for (int i=1;i<=m;i++) b[i]+=b[i-1];
		for (int i=len;i>=1;i--) sa[b[x[y[i]]]--]=y[i];
		swap(x,y); p=2; x[sa[1]]=1;
		for (int i=2;i<=len;i++)
		 x[sa[i]]=cmp(sa[i-1],sa[i],k)?p-1:p++;
		if (p>len) break;
		m=p+1;
	}
	p=0;
	for (int i=1;i<=len;i++) rank[sa[i]]=i;
	for (int i=1;i<=len;i++) {
		if (rank[i]==1) continue;
		int j=sa[rank[i]-1];
		while (i+p<=len&&j+p<=len&&a[i+p]==a[j+p]) p++;
		height[rank[i]]=p;
		p=max(p-1,0);
	}
}
int pd(int x)
{
	int size=0; int last=1;
	memset(vis,0,sizeof(vis));
	if (pos[sa[1]])
	 size=1,vis[pos[sa[1]]]=1; 
	for (int i=2;i<=len;i++)
	 if (height[i]>=x) {
	 	vis[pos[sa[i]]]++;
	 	if (vis[pos[sa[i]]]==1&&pos[sa[i]]) size++;
	 }
	 else {
	 	if (size==n) return 1;
	 	for (int j=last;j<i;j++)
	 	 vis[pos[sa[j]]]=0;
	 	if (pos[sa[i]])
		  size=1,vis[pos[sa[i]]]=1;
		else size=0;
		last=i;
	 }
	if (size==n) return 1;
	return 0;
}
int main()
{
	freopen("a.in","r",stdin);
	freopen("my.out","w",stdout);
	int T;
	scanf("%d",&T);
	for (int t=1;t<=T;t++) {
		init();
		scanf("%d",&n); len=0;
		int base=200; int n1=0;
		for (int i=1;i<=n;i++) {
			scanf("%s",s+1);
			n1=strlen(s+1);
			if (i!=1) a[++len]=++base;
			for (int j=1;j<=n1;j++) a[++len]=s[j],pos[len]=i;
			a[++len]=++base;
			for (int j=n1;j>=1;j--) a[++len]=s[j],pos[len]=i;
		}
		if (n==1) {
			printf("%d\n",n1);
			continue;
		}
		get_SA();
		int l=1; int r=len; int ans=0;
		while (l<=r) {
			int mid=(l+r)/2;
			if (pd(mid)) ans=max(ans,mid),l=mid+1;
			else r=mid-1;
		}
		printf("%d\n",ans);
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值