String and Times
时间限制: 1 Sec 内存限制: 128 MB题目描述
Now you have a string consists of uppercase letters, two integers A and B. We call a substring wonderful substring when the times it appears in that string is between A and B (A ≤ times ≤ B). Can you calculate the number of wonderful substrings in that string?输入
Input has multiple test cases.For each line, there is a string S, two integers A and B.
∑Length(S)≤2×10^6,1≤A≤B≤length(S)
输出
For each test case, print the number of the wonderful substrings in a line.样例输入
AAA 2 3 ABAB 2 2
样例输出
2 3
题意:给定字符串,求出现次数在[l,r]内的不同字符串的个数。
思路:首先考虑求出现次数大于等于k次的字符串个数。后缀数组的height数组的定义为某个后缀串和它前一个后缀串(此处“前一个”意思是按照字典序的排名来的前一个)的最长公共前缀,那么height数组里若有连续的k-1个数的最小值为min,就代表有一个长度为min的字符串最少出现了k次。
#include<bits/stdc++.h> #define INF LLONG_MAX/2 #define lson (rt*2) #define rson (rt*2+1) using namespace std; const int N = 2e5+50; char s[N]; struct SuffixArray { int sa[N],rank[N],height[N],cnt[N],a1[N],a2[N],n,m,*x,*y; void sort() { for(int i=0; i<m; i++) cnt[i]=0; for(int i=0; i<n; i++) cnt[x[i]]++; for(int i=1; i<m; i++) cnt[i]+=cnt[i-1]; for(int i=n-1; i>=0; i--) sa[--cnt[x[y[i]]]]=y[i]; } void build(char *s,int c_size) { n=strlen(s); m=c_size; x=a1; y=a2; for(int i=0; i<n; i++) x[i]=s[i],y[i]=i; x[n]=y[n]=-1; sort(); for(int k=1; k<=n; k<<=1) { int p=0; for(int i=n-k; i<n; i++) y[p++]=i; for(int i=0; i<n; i++) if(sa[i]>=k) y[p++]=sa[i]-k; sort(); p=0; std::swap(x,y); x[sa[0]]=0; for(int i=1; i<n; i++) { if(y[sa[i]]!=y[sa[i-1]]||y[sa[i]+k]!=y[sa[i-1]+k]) p++; x[sa[i]]=p; } if(p+1>=n) break; m=p+1; } for(int i=0; i<n; i++) rank[sa[i]]=i; height[0]=0; int k=0; for(int i=0; i<n; i++) { if(k) k--; if(rank[i]==0) continue; int j=sa[rank[i]-1]; while(i+k<n&&j+k<n&&s[i+k]==s[j+k]) k++; height[rank[i]]=k; } } } SA; struct node { long long val; int l,r; }tree[N<<2]; void build(int rt,int l,int r) { int mid=(l+r)>>1; tree[rt].l=l,tree[rt].r=r; tree[rt].val=INF; if(l==r) { tree[rt].val=SA.height[l]; return ; } build(lson,l,mid); build(rson,mid+1,r); tree[rt].val=min(tree[lson].val,tree[rson].val); } int query(int rt,int l,int r) { int mid=(tree[rt].l+tree[rt].r)/2; if(tree[rt].l==l&&tree[rt].r==r)return tree[rt].val; if(r<=mid)return query(lson,l,r); else if(l>mid)return query(rson,l,r); else return min(query(lson,l,mid),query(rson,mid+1,r)); } long long cal(int k,int n) //求出现k次以上的字符串的个数 { long long ans=0; if(k==1) { for(int i=0;i<n;i++) ans+=(n-SA.sa[i]-SA.height[i]); return ans; } int l=1,r=1+k-2; int pre=0; while(r<n)//处理全部长度为k-1的连续区间 { int now=query(1,l,r); if(now>=pre) ans+=now-pre;//要注意减去上一个区间的贡献 pre=now; l++,r++; } return ans; } int main() { while(scanf("%s",s)!=EOF) { int L,R; scanf("%d%d",&L,&R); int ls=strlen(s); SA.build(s,255); build(1,1,ls-1); printf("%lld\n",cal(L,ls)-cal(R+1,ls)); } return 0; }View Code