poj 3415 Common Substrings (后缀数组+单调栈)

Common Substrings
Time Limit: 5000MS   Memory Limit: 65536K
Total Submissions: 10446   Accepted: 3444

Description

A substring of a string T is defined as:

T( ik)= TiTi +1... Ti+k -1, 1≤ ii+k-1≤| T|.

Given two strings AB and one integer K, we define S, a set of triples (ijk):

S = {( ijk) |  kKA( ik)= B( jk)}.

You are to give the value of |S| for specific AB and K.

Input

The input file contains several blocks of data. For each block, the first line contains one integer K, followed by two lines containing strings A and B, respectively. The input file is ended by K=0.

1 ≤ |A|, |B| ≤ 105
1 ≤ K ≤ min{|A|, |B|}
Characters of A and B are all Latin letters.

Output

For each case, output an integer |S|.

Sample Input

2
aababaa
abaabaa
1
xx
xx
0

Sample Output

22
5

Source

POJ Monthly--2007.10.06, wintokk

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

题目大意:长度不小于K的公共子串个数。

基本的思路是求字符串A的所有后缀和字符串B的所有后缀的最长公共前缀,然后将>=k的部分全部加起来。

将两个串连接,中间用分割符连接。按height值分组后,问题就转化成了快速求一个组中分属两串的后缀中有能产生多少长度不小于K的子串。我们先以A为列,我们遇到一个A的后缀就需要统计他与前面B的后缀产生的不小于K的子串的个数,如果单看一个A的后缀和一个B的后缀,产生的个数就是(height-k+1),我们现在用一个单调栈维护栈中的height值单调递增,如果新加入的height>st[top]的话,也就是说当前位置与前面B的后缀能产生的贡献是不变的,所以直接将之前记录的值sum计入答案即可。如果当前数比栈顶元素小了,那么在的基础上(height-k+1)的值就会相应的减小,减小的值为(height-st[top])*cnt[top](cnt[top]记录的是到栈顶代表的位置到前一个栈元素之间有多少B的后缀),所以我们就要弹栈。反过来对B也这么做一遍就可以了。

#include
#include
#include
#include
#define N 1000003
#define LL long long
using namespace std;
int rank[N],sa[N],a[N],b[N],xx[N],yy[N],*x,*y,height[N];
int n,len,m,p,st[N],top,k,cnt[N];
LL sum;
char s[N];
int init()
{
	memset(b,0,sizeof(b));
	memset(sa,0,sizeof(sa));
	memset(rank,0,sizeof(rank));
}
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=200;
	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 main()
{
	freopen("a.in","r",stdin);
	freopen("my.out","w",stdout);
	while (true) {
		init();
		scanf("%d",&k);
		if (!k) break;
		scanf("%s",s+1);
		n=strlen(s+1); len=0; sum=0;
		for (int i=1;i<=n;i++) a[++len]=s[i];
		a[++len]='$'; int mark=len;
		scanf("%s",s+1); n=strlen(s+1);
		for (int i=1;i<=n;i++) a[++len]=s[i];
		get_SA(); 
		top=0; sum=0; LL ans=0;
		//if (sa[1]=k) {
		 	int num=0;
		 	while (height[i]<=st[top]&&top) {
		 		sum+=(LL)(height[i]-st[top])*(LL)cnt[top];
		 		num+=cnt[top]; top--;
			 }
			st[++top]=height[i];
			if (sa[i-1]>mark) {
				sum+=(LL)(height[i]-k+1);
				cnt[top]=num+1;
			}
			else cnt[top]=num;
			if (sa[i]=k) {
		 	int num=0;
		 	while (height[i]<=st[top]&&top) {
		 		sum+=(LL)(height[i]-st[top])*(LL)cnt[top];
		 		num+=cnt[top]; top--;
			 }
			st[++top]=height[i];
			if (sa[i-1]mark) ans+=sum;
		//	cout<


你可能感兴趣的:(后缀数组,单调栈)