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

题目链接:点击查看

题目大意:给出两个字符串,再给出一个k,问两个字符串中长度大于等于k的公共子串有多少个(种类可重复)

题目分析:因为涉及到了子串问题,先用后缀数组跑出height数组来,接下来如果直接枚举两个字符串每一个位置的height来统计答案的话,时间复杂度是n*n,还是不能在规定时间内完成,当然这里提一嘴n*n的做法,可以先nlogn预处理出任意两个后缀的height的值,然后直接n*n枚举两个字符串的所有后缀,如果其height大于等于k,则贡献为height-k+1,显然是会超时的

那么我们可以从height数组的性质入手,因为有了k的长度限制,所以可以将height数组分为几个独立的组,保证每个组的height的最小值都是大于等于k的,现在对于其中一个满足条件的height组来说,因为任意两个位置的sa所代表的后缀的最长公共前缀,就是其区间内height的最小值,这样一来我们就可以针对字符串A统计贡献,当遇到字符串B的时候,一口气将贡献都给他,当然,维护字符串A的贡献时,我们需要根据height当前的最小值实时计算贡献,到这里我是没有想到的,看了别人的题解后才恍然大悟,原来这里用一个单调栈就可以轻松解决,单调栈在这里的作用是为了记录某一段区间上的A出现了几次的,我们需要维护的是一个大顶栈,也就是维护一个单调递增的序列,当遇到比栈顶小的数的时候,就需要将栈内比当前数大的元素一一出栈,清除其贡献并重新计算,因为计算贡献统一使用:height-k+1这个公式来计算的,之前栈内比较大的元素使用这个公式计算后,因为现在遇到了比其更小的元素了,所以显然之前计算的结果有一部分算多了,需要减去多余的这些贡献,就这样利用单调栈维护贡献即可

当然对字符串A统计完贡献后记得也对字符串B统计一遍贡献,因为在处理时先A后B和先B后A将会是两种情况

代码:

#include<iostream>
#include<cstdio> 
#include<string>
#include<ctime>
#include<cstring>
#include<algorithm>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<sstream>
using namespace std;

typedef long long LL;

const int inf=0x3f3f3f3f;

const int N=2e5+100;

vector<int>ans;

char str[N];

int Stack[N],cnt[N];

int sa[N]; //SA数组,表示将S的n个后缀从小到大排序后把排好序的
//的后缀的开头位置顺次放入SA中
int t1[N],t2[N],c[N];

int rk[N],height[N],belong[N],len,k,mark;

int s[N];

bool vis[110];
 
void build_sa(int s[],int n,int m)//n为添加0后的总长
{
    int i,j,p,*x=t1,*y=t2;
    for(i=0;i<m;i++) 
		c[i]=0;
    for(i=0;i<n;i++) 
		c[x[i]=s[i]]++;
    for(i=1;i<m;i++) 
		c[i]+=c[i-1];
    for(i=n-1;i>=0;i--) 
		sa[--c[x[i]]]=i;
    for(j=1;j<=n;j<<=1) 
	{
        p=0;
        for(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<m;i++) 
			c[i]=0;
        for(i=0;i<n;i++) 
			c[x[y[i]]]++;
        for(i=1;i<m;i++) 
			c[i]+=c[i-1];
        for(i=n-1;i>=0;i--) 
			sa[--c[x[y[i]]]]=y[i];
        swap(x,y);
        p=1,x[sa[0]]=0;
        for(i=1;i<n;i++) 
            x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+j]==y[sa[i]+j]?p-1:p++;
        if(p>=n) 
			break;
        m=p;
    }
}
 
void get_height(int s[],int n)//n为添加0后的总长
{
    int i,j,k=0;
    for(i=0;i<=n;i++)
        rk[sa[i]]=i;
    for(i=0;i<n;i++) 
	{
        if(k) 
			k--;
        j=sa[rk[i]-1];
        while(s[i+k]==s[j+k]) 
			k++;
        height[rk[i]]=k;
    }
}

void solve(int base=128)
{
	build_sa(s,len+1,base);
	get_height(s,len);
}

LL cal(bool state)
{
	LL sum=0,ans=0;
	int top=0;
	for(int i=2;i<=len;i++)
	{
		if(height[i]<k)
		{
			sum=top=0;
		}
		else
		{
			int num=0;
			if(state&&sa[i-1]<mark||!state&&sa[i-1]>mark)
			{
				num++;
				sum+=height[i]-k+1;
			}
			while(top&&Stack[top]>height[i])
			{
				sum-=1LL*cnt[top]*(Stack[top]-k+1);
				sum+=1LL*cnt[top]*(height[i]-k+1);
				num+=cnt[top];
				top--;
			}
			if(num)
			{
				top++;
				Stack[top]=height[i];
				cnt[top]=num;
			}
			if(state&&sa[i]>mark||!state&&sa[i]<mark)
				ans+=sum;
		}
	}
	return ans;
}

int main()
{
//	freopen("input.txt","r",stdin);
//	ios::sync_with_stdio(false);
	while(scanf("%d",&k)!=EOF&&k)
	{
		scanf("%s",str);
		mark=strlen(str);
		str[mark]=1;
		scanf("%s",str+mark+1);
		len=strlen(str);
		for(int i=0;i<=len;i++)
			s[i]=str[i];
		solve();
		printf("%lld\n",cal(true)+cal(false));
	}







	
	
	
	
	
	
	
	
	
	return 0;
}
发布了558 篇原创文章 · 获赞 16 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_45458915/article/details/104031754