后缀数组(长度不小于k的公共子串的个数)
来源:互联网 发布:thinkphp nginx 配置 编辑:程序博客网 时间:2024/04/30 02:12
POJ 3415 Common Substrings
题意:长度不小于k的公共子串的个数;
思路:基本思路是计算A的所有后缀和B的所有后缀之间的最长公共前缀的长度,把最长公共前缀长度不小于k的部分全部加起来。 先将两个字符串连起来, 中间用一个没有出现过的字符隔开。按height值分组后,接下来的工作便是快速的统计每组中后缀之间的最长公共前缀之和。 扫描一遍, 每遇到一个B的后缀就统计与前面的A的后缀能产生多少个长度不小于k的公共子串, 这里A的后缀需要用一个单调的栈来高效的维护。 然后对A也这样做一次;
在这里提一下如何用单调栈维护,假如现在是维护A的后缀,找到一个A的后缀就将其入栈,但是如果将要入栈的这个的height值比栈里面的小则需要更新栈里面的的值,假如栈里面现在有两个值,分别为4 5,现在有一个值为3的要进栈,则需要将前两个的值更新为3,因为如果后面有B的后缀出现,则B与前面的A的后缀的最长公共前缀必定不会大于3;所以要更新为3,但是这样栈里就会有3个相同的值都为3,因此给每个值赋予一个权值代表里面有几个则可以批量更新,当遇到B后缀的时候记得也要维护一次单调栈,因为到B如果height值更小了,则结果将更小,维护完之后统计一下就好了;
/*** 倍增算法(n*logn)* 待排序数组长度为n,放在0~n-1中,在最后补0* sa为后缀数组,把后缀从小到大排序把后缀开头存起来,rank为名次数组,以i开头的后缀在所有后缀中排第几* sa的有效值为1~n,sa[0]必为n无效* rank的有效值为0~n-1,rank[n]必为0无效* height的有效值为2~n,前两个为0**/#include<cstdio>#include<cstring>#include<cstdlib>#include<iostream>#include<algorithm>#define F(x) ((x)/3+((x)%3==1?0:tb))#define G(x) ((x)<tb?(x)*3+1:((x)-tb)*3+2)using namespace std;const int maxn=1e6+10;int x[maxn*3];int wa[maxn*3],wb[maxn*3],ww[maxn*3],wv[maxn*3],nn,Z=1,kk;char str[100010];int cmp(int *r,int a,int b,int l){ return r[a]==r[b]&&r[a+l]==r[b+l];}void da(int *r,int *sa,int n,int m)//求的数组,得到的后缀数组,最长长度+1,数组里的最大值(一般180或者255);{ int i,j,p,*x=wa,*y=wb,*t; for(i=0; i<m; i++) ww[i]=0; for(i=0; i<n; i++) ww[x[i]=r[i]]++; for(i=1; i<m; i++) ww[i]+=ww[i-1]; for(i=n-1; i>=0; i--) sa[--ww[x[i]]]=i; //处理长度为一的字符串,得到sa数组 for(j=1,p=1; p<n; j*=2,m=p) //倍增法求sa { for(p=0,i=n-j; i<n; i++) y[p++]=i; for(i=0; i<n; i++) if(sa[i]>=j) y[p++]=sa[i]-j;//利用上次的sa直接求出按第二个关键字排序 for(i=0; i<n; i++) wv[i]=x[y[i]]; //第二关键字的排序得出第一关键字的顺序 for(i=0; i<m; i++) ww[i]=0; for(i=0; i<n; i++) ww[wv[i]]++; for(i=1; i<m; i++) ww[i]+=ww[i-1]; for(i=n-1; i>=0; i--) sa[--ww[wv[i]]]=y[i]; //根据第一关键字的顺序排出sa数组的顺序 for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1; i<n; i++) //更新x数组 x为rank数组 x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++; } return ;}int c0(int *r,int a,int b){ return r[a]==r[b]&&r[a+1]==r[b+1]&&r[a+2]==r[b+2];}int c12(int k,int *r,int a,int b){ if(k==2) return r[a]<r[b]||(r[a]==r[b]&&c12(1,r,a+1,b+1)); else return r[a]<r[b]||(r[a]==r[b]&&wv[a+1]<wv[b+1]);}void Sort(int *r,int *a,int *b,int n,int m){ int i; for(i=0; i<n; i++) wv[i]=r[a[i]]; for(i=0; i<m; i++) ww[i]=0; for(i=0; i<n; i++) ww[wv[i]]++; for(i=1; i<m; i++) ww[i]+=ww[i-1]; for(i=n-1; i>=0; i--) b[--ww[wv[i]]]=a[i]; return ;}void dc3(int *r,int *sa,int n,int m)//dc3比倍增法快一点{ int i,j,*rn=r+n,*san=sa+n,ta=0,tb=(n+1)/3,tbc=0,p; r[n]=r[n+1]=0; for(i=0; i<n; i++) if(i%3!=0) wa[tbc++]=i; Sort(r+2,wa,wb,tbc,m); Sort(r+1,wb,wa,tbc,m); Sort(r,wa,wb,tbc,m); for(p=1,rn[F(wb[0])]=0,i=1; i<tbc; i++) rn[F(wb[i])]=c0(r,wb[i-1],wb[i])?p-1:p++; if(p<tbc) dc3(rn,san,tbc,p); else for(i=0; i<tbc; i++) san[rn[i]]=i; for(i=0; i<tbc; i++) if(san[i]<tb) wb[ta++]=san[i]*3; if(n%3==1) wb[ta++]=n-1; Sort(r,wb,wa,ta,m); for(i=0; i<tbc; i++) wv[wb[i]=G(san[i])]=i; for(i=0,j=0,p=0; i<ta&&j<tbc; p++) sa[p]=c12(wb[j]%3,r,wa[i],wb[j])?wa[i++]:wb[j++]; for(; i<ta; p++) sa[p]=wa[i++]; for(; j<tbc; p++) sa[p]=wb[j++]; return ;}int h[maxn*3];//也就是排名相邻的两个后缀的最长公共前缀sa[i]和sa[i-1]int Rank[maxn*3];//名次数组void get_height(int *r,int *sa,int n)//同上,n小1{ int k=0,i,j; for(int i=1; i<=n; i++) Rank[sa[i]]=i; for(int i=0; i<n; h[Rank[i++]]=k) for(k?k--:0,j=sa[Rank[i]-1]; r[i+k]==r[j+k]; k++) ; return ;}int a[maxn*3];int sa[maxn*3],r[maxn*3];int s[maxn][2];int solve1(int len,int len1){ int n=len+len1+1; long long sum=0,cont=0,top=0,tot=0; for(int i=1;i<=n;i++)//维护a串 { if(h[i]<kk) tot=0,top=0;//不能达到分组条件 else { cont=0; if(sa[i-1]<len) cont++,tot+=h[i]-kk+1;//是a串的后缀 while(top>0&&h[i]<=s[top-1][0])//维护单调栈 { top--; tot-=s[top][1]*(s[top][0]-h[i]);//令小的值更新前面的,如果h[i]减小则前面提供给后面后面的b的后缀的子串也相应的减少 cont+=s[top][1];//累加前面的值用于统一更新 } s[top][0]=h[i],s[top++][1]=cont;//a串后缀入栈,cont为1,b串后缀入队cont为0 if(sa[i]>len) sum+=tot;//b串的后缀,统计前面的结果 } } tot=0,top=0; for(int i=1;i<=n;i++)//维护b串 { if(h[i]<kk) tot=0,top=0; else { cont=0; if(sa[i-1]>len) cont++,tot+=h[i]-kk+1; while(top>0&&h[i]<=s[top-1][0]) { top--; tot-=s[top][1]*(s[top][0]-h[i]); cont+=s[top][1]; } s[top][0]=h[i],s[top++][1]=cont; if(sa[i]<len) sum+=tot; } } printf("%lld\n",sum);}int main(){ while(~scanf("%d",&kk)&&kk) { string a[2]; cin>>a[0]>>a[1]; int len=a[0].size(); for(int i=0;i<len;i++) x[i]=a[0][i]; x[len]=1; int len1=a[1].size(); for(int i=0;i<len1;i++) x[1+i+len]=a[1][i]; x[len+len1+1]=0; nn=len+len1+1; da(x,sa,nn+1,255); get_height(x,sa,nn); solve1(len,len1); }}
0 0
- 后缀数组(长度不小于k的公共子串的个数)
- 后缀数组(长度不小于k的公共子串的个数)
- hdu 3415 后缀数组 长度不小于 k 的公共子串的个数
- poj 3415 ( 后缀数组 长度不小于 k 的公共子串的个数)
- poj 3415 :长度不小于 k 的公共子串的个数(后缀数组+单调栈)
- POJ - 3415 Common Substrings(后缀数组求长度不小于 k 的公共子串的个数+单调栈优化)
- POJ 3415 Common Substrings(长度不小于k 的公共子串的个数--后缀数组+单调栈优化)
- POJ 3415 求两个字符串间长度不小于k的公共子串的个数
- 长度不小于 k 的公共子串的个数(poj3415)
- poj3415之长度不小于k的公共子串个数
- POJ 3415 不小于k的公共子串的个数
- (Relax 后缀数组1.3)POJ 3415 Common Substrings(求串A和串B中长度不小于k的公共子串数)
- poj 3294 不小于 k 个字符串中的最长子串(后缀数组+二分)
- 后缀数组(不小于k个字符串中的最长子串)
- POJ 题目3415 Common Substrings(后缀数组+栈,求可以匹配到的长度大于k的公共子串个数)
- HUST 1352 求重复次数不小于k的子串的个数。
- poj 3415 长度超过K的公共子串个数
- POJ 3294 Life Forms(不小于k个字符串中的最长子串 后缀数组)
- 花样灯
- 为ios工程添加 .dylib库 的方法,libz.dylib libsqlite3.dylib等等
- Android 自定义view第二弹——组合控件
- android studio主题设置
- SpringBoot @EnableAutoConfiguration原理
- 后缀数组(长度不小于k的公共子串的个数)
- B. One Bomb
- sockaddr_in , sockaddr , in_addr区别Socket编程函数集(非常有用)
- 【Unity】SQLite在Unity中的使用-思维导图
- /dev/mem可没那么简单
- php协程(Coroutine)学习笔记
- Drools规则引擎(二)-Drools-Example
- Chrome上面按住Shift+鼠标左键双击会直接弹出“检查”代码的窗口
- 以太坊虚拟机实现分析