POJ-Common Substrings(后缀数组-长度不小于 k 的公共子串的个数)
2013-09-08 16:08
453 查看
题意:
长度不小于 k 的公共子串的个数
分析:
基本思路是计算 A 的所有后缀和 B 的所有后缀之间的最长公共前缀的长度,把最长公共前缀长度不小于 k 的部分全部加起来。
先将两个字符串连起来,中间用一个没有出现过的字符隔开。按 height 值分组后,接下来的工作便是快速的统计每组中后缀之间的最长公共前缀之和。
扫描一遍,每遇到一个 B 的后缀就统计与前面的 A 的后缀能产生多少个长度不小于 k 的公共子串,
这里 A 的后缀需要用一个单调的栈来高效的维护。然后对 A 也这样做一次。
长度不小于 k 的公共子串的个数
分析:
基本思路是计算 A 的所有后缀和 B 的所有后缀之间的最长公共前缀的长度,把最长公共前缀长度不小于 k 的部分全部加起来。
先将两个字符串连起来,中间用一个没有出现过的字符隔开。按 height 值分组后,接下来的工作便是快速的统计每组中后缀之间的最长公共前缀之和。
扫描一遍,每遇到一个 B 的后缀就统计与前面的 A 的后缀能产生多少个长度不小于 k 的公共子串,
这里 A 的后缀需要用一个单调的栈来高效的维护。然后对 A 也这样做一次。
// File Name: 3415.cpp // Author: Zlbing // Created Time: 2013年09月07日 星期六 14时58分18秒 #include<iostream> #include<string> #include<algorithm> #include<cstdlib> #include<cstdio> #include<set> #include<map> #include<vector> #include<cstring> #include<stack> #include<cmath> #include<queue> using namespace std; #define CL(x,v); memset(x,v,sizeof(x)); #define INF 0x3f3f3f3f #define LL long long #define REP(i,r,n) for(int i=r;i<=n;i++) #define RREP(i,n,r) for(int i=n;i>=r;i--) //rank从0开始 //sa从1开始,因为最后一个字符(最小的)排在第0位 //height从2开始,因为表示的是sa[i-1]和sa[i] const int MAXN=220000; int rank[MAXN],sa[MAXN],X[MAXN],Y[MAXN],height[MAXN]; char s[MAXN]; int buc[MAXN]; void calheight(int n) { int i , j , k = 0; for(i = 1 ; i <= n ; i++) rank[sa[i]] = i; for(i = 0 ; i < n ; height[rank[i++]] = k) for(k?k--:0 , j = sa[rank[i]-1] ; s[i+k] == s[j+k] ; k++); } bool cmp(int *r,int a,int b,int l) { return (r[a] == r[b] && r[a+l] == r[b+l]); } void suffix(int n,int m = 128) { int i , l , p , *x = X , *y = Y; for(i = 0 ; i < m ; i ++) buc[i] = 0; for(i = 0 ; i < n ; i ++) buc[ x[i] = s[i] ] ++; for(i = 1 ; i < m ; i ++) buc[i] += buc[i-1]; for(i = n - 1; i >= 0 ; i --) sa[ --buc[ x[i] ]] = i; for(l = 1,p = 1 ; p < n ; m = p , l *= 2) { p = 0; for(i = n-l ; i < n ; i ++) y[p++] = i; for(i = 0 ; i < n ; i ++) if(sa[i] >= l) y[p++] = sa[i] - l; for(i = 0 ; i < m ; i ++) buc[i] = 0; for(i = 0 ; i < n ; i ++) buc[ x[y[i]] ] ++; for(i = 1 ; i < m ; i ++) buc[i] += buc[i-1]; for(i = n - 1; i >= 0 ; i --) sa[ --buc[ x[y[i]] ] ] = y[i]; for(swap(x,y) , x[sa[0]] = 0 , i = 1 , p = 1 ; i < n ; i ++) x[ sa[i] ] = cmp(y,sa[i-1],sa[i],l) ? p-1 : p++; } calheight(n-1);//后缀数组关键是求出height,所以求sa的时候顺便把rank和height求出来 } char ch[MAXN]; LL h[MAXN],w[MAXN]; //单调栈h中每个元素如h[j]代表一个区间,这个区间内每一个后缀与当前位置i的LCP都为h[j] //w[j]表示与但前位置i的LCP为h[j]的个数 LL num[MAXN]; int top; int main() { int k; while(~scanf("%d",&k)) { if(!k)break; scanf("%s",s); scanf("%s",ch); int len1=strlen(s); int len2=strlen(ch); s[len1]=1; for(int i=len1+1;i<len1+len2+1;i++) s[i]=ch[i-len1-1]; s[len1+len2+1]=0; int n=len1+len2+1; suffix(n+1); initRMQ(n); //当两个字串的公共子串大于k时,能产生geight[i]-k+1个大于k的子串 for(int i=3;i<=n;i++) height[i]=max(0,height[i]-k+1); LL ans=0; top=0; num[0]=0; //每遇到一个A的后缀,统计与前面B的后缀能产生多少个长度不小于 k 的公共子串 for(int i=3;i<=n;i++) { if(height[i]==0) { top=0; } else { int cnt=0; for(;top&&h[top]>=height[i];top--) { cnt+=w[top]; } h[++top]=height[i]; //当sa[i-1]为B的后缀时,加1 w[top]=cnt+(sa[i-1]>len1); num[top]=num[top-1]+h[top]*w[top]; //sa[i]为A的后缀时,统计与前面B的后缀产生的公共子串 if(sa[i]<len1) { ans+=num[top]; } } } top=0; num[0]=0; for(int i=3;i<=n;i++) { if(height[i]==0) { top=0; } else { int cnt=0; for(;top&&h[top]>=height[i];top--) { cnt+=w[top]; } h[++top]=height[i]; w[top]=cnt+(sa[i-1]<len1); num[top]=num[top-1]+h[top]*w[top]; if(sa[i]>len1) { ans+=num[top]; } } } printf("%lld\n",ans); } return 0; }
相关文章推荐
- POJ - 3415 Common Substrings(后缀数组求长度不小于 k 的公共子串的个数+单调栈优化)
- (Relax 后缀数组1.3)POJ 3415 Common Substrings(求串A和串B中长度不小于k的公共子串数)
- POJ 3415 Common Substrings (后缀数组,长度不小于k的公共子串的个数)
- poj 3415 后缀数组 两个字符串中长度不小于 k 的公共子串的个数
- POJ - 3415 Common Substrings(后缀数组求长度不小于 k 的公共子串的个数+单调栈优化)
- POJ 题目2774 Long Long Message(后缀数组,求最长公共子串长度)
- POJ 3415 Common Substrings(长度不小于K的公共子串的个数+后缀数组+height数组分组思想+单调栈)
- 【后缀数组】 POJ 2774 Long Long Message 两个字符串的最长公共子串长度
- poj 3294 Life Forms(n个字符串中 求公共子串长度超过k得最大子串 后缀数组)
- 后缀数组(长度不小于k的公共子串的个数)
- 后缀数组(长度不小于k的公共子串的个数)
- POJ 2774-Long Long Message(后缀数组+高度数组-最大公共子串长度)
- (Relax 后缀数组1.4)POJ 2774 Long Long Message(求两个字符串公共子串的最大长度)
- hdu 3415 后缀数组 长度不小于 k 的公共子串的个数
- POJ 2217-Secretary(后缀数组+高度数组-最大公共子串长度)
- poj 3294 不小于 k 个字符串中的最长子串(后缀数组+二分)
- POJ 2774 Long Long Message (后缀数组求最长公共子串)
- poj 3882 后缀数组 求一个串至少出现k次的最长重复子串的长度
- poj 3294 后缀数组 多字符串中不小于 k 个字符串中的最长子串
- Long Long Message+POJ+后缀数组之求最长公共子串