HDU - 5769 (2020.3.26训练D题)

Problem
?? is practicing his program skill, and now he is given a string, he has to calculate the total number of its distinct substrings.
But ?? thinks that is too easy, he wants to make this problem more interesting.
?? likes a character X very much, so he wants to know the number of distinct substrings which contains at least one X.
However, ?? is unable to solve it, please help him.
Input
The first line of the input gives the number of test cases T;T test cases follow.
Each test case is consist of 2 lines:
First line is a character X, and second line is a string S.
X is a lowercase letter, and S contains lowercase letters(‘a’-‘z’) only.
T<=30
1<=|S|<=10^5
The sum of |S| in all the test cases is no more than 700,000.
Output
For each test case, output one line containing “Case #x: y”(without quotes), where x is the test case number(starting from 1) and y is the answer you get for that case.

题意:给出一个字符c和一个字符串,求该字符串有多少个包含该字符的不重复子字符串

这题可以使用后缀数组,一个字符串的所有子串都可以用后缀的前缀来表示,利用后缀数组处理好后的height,sa数组,遍历求解,每次都要根据height[i]和pre的大小关系分成两种情况增加答案数(pre表示上一个前缀字符串中字符串c出现的下标,初始赋值为1e5+5)

具体如下:

a
ababc

比如这个例子,排序好后的前缀字符串如下(第二列表示height[i]):
1.ababc->0
2.abc->2
3.babc->0
4.bc->1
5.c->0

比如遍历好1后,pre=1,这时开始遍历2,height[2]=2>pre,表示已经在上个串出现过,无需再次遍历,只需要ans+=(n-sa[i]-height[i]+1)就行了(我的s数组是从下标开始存的,注意)
当遍历3时,pre仍旧是1,height[i]=0<pre,这时候就需要从这个前缀字符串的起始下标开始,寻找==字符c的位置下标,叫做j,得到j后,只需要ans+=(n-j+1)
一共就这两种情况,书写时注意代码细节

AC代码:

/*数组sa表示排名i的子串下标是多少
,数组rak表示下标i的子串排名多少
,数组height表示LCP(i,i-1)最长公共前缀
,排名是按照字典序排序后的排名*/
#include<iostream>
using namespace std;
typedef long long ll;
const int N = 100010;
int wa[N], wb[N], wv[N], wss[N], rak[N], height[N], cal[N], n, sa[N];
char s[N];
int cmp(int *r, int a, int b, int l)
{return r[a]==r[b]&&r[a+l]==r[b+l];}
void da(int *r,int *sa,int n,int M) {
     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;
     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++) 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];
        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++;
     }
     return;
}
void calheight(int *r,int *sa,int n) {
     int i,j,k=0;
     for(i=1;i<=n;i++) rak[sa[i]]=i;
     for(i=0;i<n;height[rak[i++]]=k)
     for(k?k--:0,j=sa[rak[i]-1];r[i+k]==r[j+k];k++);
     for(int i=n;i;i--)rak[i]=rak[i-1],sa[i]++;
}
int main()
{
    int cas=1;
	int T;
	char flag;
	int cnt=1;
	cin>>T;
    while(T--)
	{
		cin>>flag;
		scanf("%s",s+1);
        n=strlen(s+1);
        for(int i=1;i<=n;i++)
            cal[i]=s[i];
        cal[n+1]=0;//不能少
        da(cal+1,sa,n+1,200);
        calheight(cal+1,sa,n);

		ll ans=0;
		int pre=100005;
		int j;
        for(int i=1;i<=n;i++)
		{
			if(height[i]>pre)
			{
				ans+=n-sa[i]+1-height[i];
				continue;
			}
			else
			{
				j=sa[i]+height[i];
			}
			pre=height[i];
			for(;j<=n;j++)
			{
				if(s[j]!=flag)
					pre++;
				else
					break;
			}
			//if(j==n+1) pre=100005;
			ans+=n-j+1;
		}
		printf("Case #%d: ",cnt++);
		cout<<ans<<endl;
    }
}
发布了16 篇原创文章 · 获赞 21 · 访问量 1320

猜你喜欢

转载自blog.csdn.net/rainbowower/article/details/105144696
今日推荐