poj3415Common Substrings(后缀数组+单调栈)

来源:互联网 发布:js设置背景颜色 编辑:程序博客网 时间:2024/05/28 01:34

题目大意:给两个字符串,求所有满足 长度大于等于k的公共子串 的对数。

思路:基本的,计算A 的所有后缀和B 的所有后缀之间的最长公共前缀的长度,
把最长公共前缀长度不小于k 的部分全部加起来。(显然会TLE到死),所以我们把这两个串连起来(中间用特殊字符隔开),根据h数组进行分组,保证一组内字符串之间lcp都是大于等于k的。接下来的工作便是快速的统计每组中后缀之间的最长公共前缀之和。扫描一遍,每遇到一个B 的后缀就统计与前面的A 的后缀能产生多少个长度不小于k 的公共子串,这里A 的后缀需要用一个单调的栈来高效的维护。(单调递增栈,始终维护一个sum值表示目前A对答案的贡献,每遇到B,就给答案加上sum)。然后对A 也这样做一次。


#include <cstdio>#include <cstring>#define N 200010#define ll long longint n,rank[N<<1],rank1[N],sa[N],tmp[N],h[N],count[N],len;char s[N];inline int min(int x,int y){return x<y?x:y;}int main(){//freopen("a.in","r",stdin);while(1){int K; scanf("%d",&K);if(!K) break;n=0;scanf("%s",s+1);n=strlen(s+1);s[++n]='z'+1;len=n;scanf("%s",s+n+1);n=strlen(s+1);memset(count,0,sizeof(count));memset(rank,0,sizeof(rank));memset(h,0,sizeof(h));for(int i=1;i<=n;++i) count[s[i]]=1;for(int i=1;i<=200;++i) count[i]+=count[i-1];for(int i=1;i<=n;++i) rank[i]=count[s[i]];int k=0;for(int p=1;k!=n;p<<=1){memset(count,0,sizeof(count));for(int i=1;i<=n;++i) count[rank[i+p]]++;for(int i=1;i<=n;++i) count[i]+=count[i-1];for(int i=n;i>=1;--i) tmp[count[rank[i+p]]--]=i;memset(count,0,sizeof(count));for(int i=1;i<=n;++i) count[rank[tmp[i]]]++;for(int i=1;i<=n;++i) count[i]+=count[i-1];for(int i=n;i>=1;--i) sa[count[rank[tmp[i]]]--]=tmp[i];memcpy(rank1,rank,sizeof(rank1));rank[sa[1]]=k=1;for(int i=2;i<=n;++i){if(rank1[sa[i]]!=rank1[sa[i-1]]||rank1[sa[i]+p]!=rank1[sa[i-1]+p]) ++k;rank[sa[i]]=k;}}k=0;for(int i=1;i<=n;++i){if(rank[i]==1){h[1]=0;continue;}if(i==1||h[rank[i-1]]<=1) k=0;if(k) --k;while(s[i+k]==s[sa[rank[i]-1]+k]) ++k;h[rank[i]]=k;}ll ans=0,sum=0;int top=0,stack[N],cnt[N];//单调递增栈 for(int i=1;i<=n;++i){//A与前面的B能构成的子串 if(h[i]<K){sum=top=0;continue;}int num=0;while(top&&h[i]<=stack[top]){sum-=(ll)(stack[top]-K+1)*cnt[top];sum+=(ll)(h[i]-K+1)*cnt[top];num+=cnt[top];--top;}stack[++top]=h[i];if(sa[i-1]>len) sum+=(h[i]-K+1),cnt[top]=num+1;//B才算影响 else cnt[top]=num;if(sa[i]<len) ans+=sum;//遇到A的,加答案 }for(int i=1;i<=n;++i){//B与前面的A能构成的子串 if(h[i]<K){sum=top=0;continue;}int num=0;while(top&&h[i]<=stack[top]){sum-=(ll)(stack[top]-K+1)*cnt[top];sum+=(ll)(h[i]-K+1)*cnt[top];num+=cnt[top];--top;}stack[++top]=h[i];if(sa[i-1]<len) sum+=(h[i]-K+1),cnt[top]=num+1;else cnt[top]=num;if(sa[i]>len) ans+=sum;}printf("%lld\n",ans);}return 0;}


阅读全文
0 0