字符串(后缀数组):POJ 3415 Common Substrings
Posted 既然选择了远方,便只顾风雨兼程
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了字符串(后缀数组):POJ 3415 Common Substrings相关的知识,希望对你有一定的参考价值。
Description
A substring of a string T is defined as:
Given two strings A, B and one integer K, we define S, a set of triples (i, j, k):
You are to give the value of |S| for specific A, B and K.
Input
The input file contains several blocks of data. For each block, the first line contains one integer K, followed by two lines containing strings A and B, respectively. The input file is ended by K=0.
1 ≤ |A|, |B| ≤ 105
1 ≤ K ≤ min{|A|, |B|}
Characters of A and B are all Latin letters.
Output
For each case, output an integer |S|.
Sample Input
2 aababaa abaabaa 1 xx xx 0
Sample Output
22 5
这道题呃,有些考验程序实践能力。
题意:对于给定的两个字符串和一个整数K,求两个字符串长度大于等于K的公共子串数目。
将两个字符串接起来,中间用一个特殊字符隔开,枚举Lcp,暴力枚举是O(n³)的,死活都不可能过。
这是我们想:能否使用以前枚举的信息?所以正解就出来了:单调栈优化!
具体咋打就看代码吧~~~
1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 using namespace std; 5 const int maxn=400010; 6 char S[maxn]; 7 int sa[maxn],r[maxn],rank[maxn],lcp[maxn]; 8 int Wv[maxn],Ws[maxn],Wa[maxn],Wb[maxn],len; 9 10 bool cmp(int *p,int a,int b,int l){ 11 return p[a]==p[b]&&p[a+l]==p[b+l]; 12 } 13 14 void DA(int n,int m){ 15 int i,j,p,*x=Wa,*y=Wb,*t; 16 for(i=0;i<m;i++)Ws[i]=0; 17 for(i=0;i<n;i++)++Ws[x[i]=r[i]]; 18 for(i=1;i<m;i++)Ws[i]+=Ws[i-1]; 19 for(i=n-1;i>=0;i--)sa[--Ws[x[i]]]=i; 20 21 for(j=1,p=1;p<n;m=p,j<<=1){ 22 for(p=0,i=n-j;i<n;i++)y[p++]=i; 23 for(i=0;i<n;i++) 24 if(sa[i]>=j) 25 y[p++]=sa[i]-j; 26 27 for(i=0;i<m;i++)Ws[i]=0; 28 for(i=0;i<n;i++)++Ws[Wv[i]=x[y[i]]]; 29 for(i=1;i<m;i++)Ws[i]+=Ws[i-1]; 30 for(i=n-1;i>=0;i--) 31 sa[--Ws[Wv[i]]]=y[i]; 32 33 for(t=x,x=y,y=t,i=1,p=1,x[sa[0]]=0;i<n;i++) 34 x[sa[i]]=cmp(y,sa[i],sa[i-1],j)?p-1:p++; 35 } 36 } 37 38 void Lcp(int n){ 39 int i,j,k=0; 40 for(i=1;i<=n;i++)rank[sa[i]]=i; 41 for(i=0;i<n;lcp[rank[i++]]=k) 42 for(k?--k:k,j=sa[rank[i]-1];r[i+k]==r[j+k];++k); 43 } 44 45 int s[maxn][2]; 46 47 int main(){ 48 int n,k; 49 while(~scanf("%d",&k)&&k){ 50 scanf("%s",S); 51 n=strlen(S);S[n]=‘%‘; 52 scanf("%s",S+n+1); 53 len=strlen(S); 54 for(int i=0;i<len;i++) 55 r[i]=S[i]; 56 r[len]=0; 57 DA(len+1,128); 58 Lcp(len); 59 60 int cnt=0; 61 long long ans=0,sum=0; 62 for(int i=1;i<=len;i++){ 63 if(lcp[i]<k){ 64 sum=0;cnt=0; 65 continue; 66 } 67 int tot=0; 68 if(sa[i-1]>n){ 69 sum+=lcp[i]-k+1; 70 tot++; 71 } 72 while(cnt&&s[cnt][0]>=lcp[i]){ 73 tot+=s[cnt][1]; 74 sum-=1ll*s[cnt][1]*(s[cnt][0]-lcp[i]); 75 cnt--; 76 } 77 s[++cnt][0]=lcp[i]; 78 s[cnt][1]=tot; 79 if(sa[i]<n)ans+=sum; 80 } 81 cnt=0;sum=0; 82 for(int i=1;i<=len;i++){ 83 if(lcp[i]<k){ 84 sum=0;cnt=0; 85 continue; 86 } 87 int tot=0; 88 if(sa[i-1]<n){ 89 sum+=lcp[i]-k+1; 90 tot++; 91 } 92 while(cnt&&s[cnt][0]>=lcp[i]){ 93 tot+=s[cnt][1]; 94 sum-=1ll*s[cnt][1]*(s[cnt][0]-lcp[i]); 95 cnt--; 96 } 97 s[++cnt][0]=lcp[i]; 98 s[cnt][1]=tot; 99 if(sa[i]>n)ans+=sum; 100 } 101 printf("%lld\n",ans); 102 } 103 return 0; 104 }
以上是关于字符串(后缀数组):POJ 3415 Common Substrings的主要内容,如果未能解决你的问题,请参考以下文章
POJ3415 Common Substrings 后缀数组 + 单调栈
Common Substrings POJ - 3415 (后缀数组 + 单调栈)