HDU5769 Substring(后缀数组)
题目大概说给一个字符串问有几个不同且包含给定某个字符子串。
如果不考虑子串一定要包含给定的字符,那样容易想到用后缀数组解决(好像写过这种题。。)。。因为每个子串都是某个后缀的前缀。
求出的后缀有序排列后,考虑各个后缀能贡献几个前缀,这个就是各个后缀有几个前缀满足不和前面统计过的后缀的前缀相同,其实这个贡献就等于后缀长度减去与上一个后缀的LCP(height)。
而考虑一下一定要包含给定字符的情况:如果字符串某个地方出现了该字符,那么所有大于等于该字符位置的前缀都包含该字符。
这样就能想到预处理出所有后缀中第一个出现该字符的位置,idx[i],这个从最后一个后缀开始往前转移就能推出所有后缀的情况了,即:
- idx[i]=0(str[i]=0)
- idx[i]=idx[i+1]+1(idx[i+1]存在)
- idx[i]=不存在(其他情况)
最后,可以再考虑一下给定字符包含在LCP里面,或者不在LCP里面的情况。。综合起来,各个后缀i的贡献就等于 len[i]-max(height[i],idx[i])。
另外,比赛时的编译器是C++11还是什么,后缀数组模板里的rank与std里面的一个rank()冲突了,编译错误。。。得改一改模板。。
1 #include<cstdio> 2 #include<cstring> 3 #include<cmath> 4 #include<algorithm> 5 using namespace std; 6 #define INF (1<<30) 7 #define MAXN 111111 8 9 int wa[MAXN],wb[MAXN],wv[MAXN],ws[MAXN]; 10 int cmp(int *r,int a,int b,int l){ 11 return r[a]==r[b] && r[a+l]==r[b+l]; 12 } 13 int sa[MAXN],rnk[MAXN],height[MAXN]; 14 void SA(int *r,int n,int m){ 15 int *x=wa,*y=wb; 16 17 for(int i=0; i<m; ++i) ws[i]=0; 18 for(int i=0; i<n; ++i) ++ws[x[i]=r[i]]; 19 for(int i=1; i<m; ++i) ws[i]+=ws[i-1]; 20 for(int i=n-1; i>=0; --i) sa[--ws[x[i]]]=i; 21 22 int p=1; 23 for(int j=1; p<n; j<<=1,m=p){ 24 p=0; 25 for(int i=n-j; i<n; ++i) y[p++]=i; 26 for(int i=0; i<n; ++i) if(sa[i]>=j) y[p++]=sa[i]-j; 27 for(int i=0; i<n; ++i) wv[i]=x[y[i]]; 28 for(int i=0; i<m; ++i) ws[i]=0; 29 for(int i=0; i<n; ++i) ++ws[wv[i]]; 30 for(int i=1; i<m; ++i) ws[i]+=ws[i-1]; 31 for(int i=n-1; i>=0; --i) sa[--ws[wv[i]]]=y[i]; 32 swap(x,y); x[sa[0]]=0; p=1; 33 for(int i=1; i<n; ++i) x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++; 34 } 35 36 for(int i=1; i<n; ++i) rnk[sa[i]]=i; 37 int k=0; 38 for(int i=0; i<n-1; height[rnk[i++]]=k){ 39 if(k) --k; 40 for(int j=sa[rnk[i]-1]; r[i+k]==r[j+k]; ++k); 41 } 42 } 43 44 int idx[MAXN]; 45 46 int seq[MAXN]; 47 char str[MAXN]; 48 int main(){ 49 char ch; 50 int t; 51 scanf("%d",&t); 52 for(int cse=1; cse<=t; ++cse){ 53 scanf(" %c%s",&ch,str); 54 55 int totlen=strlen(str); 56 57 for(int i=0; i<totlen; ++i){ 58 seq[i]=str[i]-'a'+1; 59 } 60 seq[totlen]=0; 61 62 SA(seq,totlen+1,27); 63 64 idx[totlen-1] = (str[totlen-1]==ch) ? 0 : INF; 65 for(int i=totlen-2; i>=0; --i){ 66 if(str[i]==ch) idx[i]=0; 67 else if(idx[i+1]==INF) idx[i]=INF; 68 else idx[i]=idx[i+1]+1; 69 } 70 71 long long ans=0; 72 73 for(int i=1; i<=totlen; ++i){ 74 if(idx[sa[i]]==INF) continue; 75 if(i==1){ 76 ans+=(totlen-sa[i])-idx[sa[i]]; 77 }else{ 78 ans+=(totlen-sa[i])-max(height[i],idx[sa[i]]); 79 } 80 } 81 82 printf("Case #%d: %I64d\n",cse,ans); 83 } 84 return 0; 85 }