HDU 6194 string string string [后缀数组]

题意:给你长度为n的串,问这个串的子串出现次数刚好等于k的数量。

题解:后缀数组跑一遍,得到height数组,由于height数组记录的是这个串与前一个串的最长公共前缀,所以我们枚举左端点,得到长度为k-1区间内的height数组,得到区间最小值,这个最小值代表,子串出现次数大于等于k的总个数,然后得到向前k+1长度时候的结果和向后k+1时候的结果表示子串出现次数大于的等于k+1时候的结果,然后大于等于k的结果去减去大于等于k+1时候的结果,就是子串出现刚好k次的数量。

注意:当k等于1的时候要特判。

AC代码:

 

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN =(int)1e6+10;
int wa[MAXN],wb[MAXN],wv[MAXN],we[MAXN],rk[MAXN];
int cmp(int *r,int a,int b,int l){return r[a]==r[b]&&r[a+l]==r[b+l];}
void build_sa(int *r,int *sa,int n,int m){
	int i,j,p,*x=wa,*y=wb,*t;
	for(i=0;i<m;i++)we[i]=0;
	for(i=0;i<n;i++)we[x[i]=r[i]]++;
	for(i=1;i<m;i++)we[i]+=we[i-1];
	for(i=n-1;i>=0;i--)sa[--we[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++)we[i]=0;
		for(i=0;i<n;i++)we[wv[i]]++;
		for(i=1;i<m;i++)we[i]+=we[i-1];
		for(i=n-1;i>=0;i--)sa[--we[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 height[MAXN];
void calheight(int *r,int *sa,int n){
	int i,j,k=0;
	for(i=1;i<=n;i++)rk[sa[i]]=i;
	for(i=0;i<n;height[rk[i++]]=k){
		for(k?k--:0,j=sa[rk[i]-1];r[i+k]==r[j+k];k++);
	}
}
int sa[MAXN],a[MAXN];
char str[MAXN];
  
int rmq[MAXN][20];    
int mm[MAXN];//最大的小于等于i的2^mm[i]   
void initRMQ(int n)  
{  
    mm[0]=-1;  
    for(int i=1;i<=n;i++)  
    {  
        mm[i]=((i&(i-1))==0)?mm[i-1]+1:mm[i-1];  
        rmq[i][0]=height[i];  
    }  
    for(int j=1;j<=mm[n];j++)  
        for(int i=1;i+(1<<j)-1<=n;i++)  
            rmq[i][j]=min(rmq[i][j-1],rmq[i+(1<<(j-1))][j-1]);  
}  
int RMQ(int x,int y)  
{  
    int k=mm[y-x+1];  
    return min(rmq[x][k],rmq[y-(1<<k)+1][k]);  
}  
int main()
{
	int T;
	scanf("%d",&T);
	while(T--)
	{
		int k;
		scanf("%d",&k);
		scanf("%s",str);
		int n=strlen(str);
		for(int i=0;i<MAXN;i++)height[i]=0;
		for(int i=0;i<n;i++)a[i]=str[i];
		a[n]=0;
		build_sa(a,sa,n+1,128);
		calheight(a,sa,n);
		initRMQ(n);
		if(k==1)
		{
			ll ans=n*(n+1)/2;
			for(int i=1;i<=n;i++)
				ans-=max(height[i],height[i+1]);
			printf("%lld\n",ans);
			continue;
		}
		k--;
		ll ans=0;
		for(int i=2;i+k-1<=n;i++)
		{
			int sum=RMQ(i,i+k-1);
			int biao=0;
			biao=max(biao,RMQ(i-1,i+k-1));
			if(i+k<=n)biao=max(biao,RMQ(i,i+k));
			sum-=biao;
			ans+=sum;
		}
		printf("%lld\n",ans);
	}
	return 0;
}

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值