(多校第四场1006)HDU5769 Substring 后缀数组

套了一个后缀数组的模板,求出sa和height数组后,对于每一个后缀字符串,他能产生的不同子串的数量就是len-sa[i]-height[i];

而本题还要求必须包含目的字符tar,这就需要另外一个数组haha,haha[i]表示从第i个字符向后几位找到tar,例如abbabb,tar=a,则haha{0,2,1,0,2,1,0};

而对与每个后缀字符串能产生的包含tar的不通字符串个数就是len-sa[i]-max(height[i],haha[sa[i]]);累加就是了。


#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
#include<stack>
#include<cstring>
#include<map>
#include<string>
#define LL long long
#define maxn 110000
#define clean(a) memset(a,0,sizeof(a))
using namespace std;
int haha[maxn];

/**************SA模板*******************/
/*
 * sa数组和height数组都是从1开始到n结束
 */
//#define clean(a) memset(a,0,sizeof(a))
//#define maxn 110000
char str[maxn];
int _rank[maxn],height[maxn];
int sa[maxn],wa[maxn],wb[maxn],wv[maxn],wss[maxn];
int cmp(int *r,int a,int b,int l)
{
	return (r[a]==r[b]&&r[a+l]==r[b+l]);
}

//倍增算法
void da(char *r ,int n,int m)
{
	n+=1;
	int i, j, p, *x = wa, *y = wb, *t;
	//基数排序
	for (i = 0; i < m; i++) wss[i] = 0;
	for (i = 0; i < n; i++) wss[x[i] = r[i]]++;
	for (i = 1; i < m; i++) wss[i] += wss[i - 1];
	for (i = n - 1; i >= 0; i--) sa[--wss[x[i]]] = i;

	// 在第一次排序以后,_rank数组中的最大值小于p,所以让m=p。整个倍增算法基本写好,代码大约25行。
	for (j = 1, p = 1; p < n; j *= 2, m = p)
	{
		//接下来进行若干次基数排序,在实现的时候,这里有一个小优化。基数排序要分两次,第一次是对第二关键字排序,第二次是对第一关键字排序。对第二关键字排序的结果实际上可以利用上一次求得的sa直接算出,没有必要再算一次
		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;
		//其中变量j是当前字符串的长度,数组y保存的是对第二关键字排序的结果。然后要对第一关键字进行排序,
		for (i = 0; i < n; i++) wv[i] = x[y[i]];
		for (i = 0; i < m; i++) wss[i] = 0;
		for (i = 0; i < n; i++) wss[wv[i]]++;
		for (i = 1; i < m; i++) wss[i] += wss[i - 1];
		for (i = n - 1; i >= 0; i--) sa[--wss[wv[i]]] = y[i];

		//这样便求出了新的sa值。在求出sa后,下一步是计算_rank值。
		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++;
	}
}


//得到height数组:排名相邻的两个后缀的最长公共前缀
void calheight(char *r,int *sa,int n)
{
     int  k=0;
     for(int i=1;i<=n;i++)
    	 _rank[sa[i]]=i;
     for(int i=0;i<n;i++)
     {
         if(_rank[i]==0)
             continue;
         k=max(0,k-1);
         int j=sa[_rank[i]-1];
         while(r[i+k]==r[j+k])
             k++;
         height[_rank[i]]=k;
     }
     return;
}

/*********************************/
void gethaha(int len,char tar)
{
    memset(haha,0,sizeof(haha));
    haha[len]=0;
    for(int i=len-1;i>=0;--i)
    {
        if(str[i]==tar)
            haha[i]=0;
        else
            haha[i]=haha[i+1]+1;
    }
}

LL getHaveTarDistinctSubstr(int len)
{
	LL ans=0;
    for(int i=1;i<=len;i++)
        ans+=len-sa[i]-max(height[i],haha[sa[i]]);
    return ans;
}

LL getDistinctSubstr(int len)
{
	LL ans=0;
    for(int i=1;i<=len;i++)
        ans+=len-sa[i]-height[i];
    return ans;
}
int main()
{
    int T,I=1;
    scanf("%d",&T);
    char tar[12];
    while(T--)
    {
        scanf("%s%s",tar,str);
        int len=strlen(str);
        da(str,len,128);
//        for(int i=1;i<=len;++i)
//        	cout<<sa[i]<<"  ";
//        cout<<endl;
        calheight(str,sa,len);
//        for(int i=1;i<=len;++i)
//        	cout<<height[i]<<"   ";
//        cout<<endl;
        gethaha(len,tar[0]);
        LL ans=getHaveTarDistinctSubstr(len);
        printf("Case #%d: %lld\n",I++,ans);
    }
    return 0;
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值