POJ 题目3415 Common Substrings(后缀数组+栈,求可以匹配到的长度大于k的公共子串个数)
2015-08-25 19:25
537 查看
Common Substrings
Description
A substring of a string T is defined as:
T(i, k)=TiTi+1...Ti+k-1, 1≤i≤i+k-1≤|T|.
Given two strings A, B and one integer K, we define
S, a set of triples (i, j, k):
S = {(i, j, k) | k≥K,
A(i, k)=B(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
Sample Output
Source
POJ Monthly--2007.10.06, wintokk
题目大意:
就是可以匹配到的长度大于k的公共子串个数,即S = {(i, j, k) |
k≥K, A(i, k)=B(j,
k)}.个数
例如第二组,长度为1时,第一个串取第一个x,第二个串可以去第一个或第二个,种类为2,第一个串取第二个一样,种类又是2,对于长度为2时,第一个串全取,第二个串全取,一种,总共有五种
思路:按题目的意思可以转换为求所有的i ,j,两个串后缀的最长公共前缀长度lcp(s1[i],s2[j])-k+1>0的和,暴力绝对超时,所以运用一个单调栈来求所有串和这个串的最长前缀
剩下的思路讲解来自http://m.blog.csdn.net/blog/u012866104/38725689
[b]这道题需要对height数组分组后,用单调栈优化。对于LCP=L>K的前缀,对答案的贡献是L-K+1.即长度为K,K+1.....L的公共字串。对于每一组,栈里维护height值递增,这样保证了每个height的贡献量为height[i]-K+1,因为有定理LCP(i,j)=min(height[i+1],...height[j])如果在height[i]之后插入一个height[k]<height[i],那么height[i]就等于废了,他的价值仅为height[k]了,对于之后的j,LCP(i,j)起码要<=height[k],像这种递减的值插入栈前,需要将栈里>=插入值的变成插入值,然后将插入值的宽度增加。因为这当中为了降低复杂度,用了一个sum来维护当前栈里的后缀与将要入栈的后缀的公共字串的个数。因此实际上的{[b]将栈里>=插入值的变成插入值}的操作,体现在sum上,是对sum减去一段height之差*宽度。[/b][/b]
[b][b]补充:因为rank=i的后缀能与其rank前后的后缀都可能产生公共子串,但此算法只维护其前面的,因此排在A串后缀后面的B串后缀,应该用扫描B串后缀来维护,因为B在A后,以B为参考系,A就在前,这就是为什么要分别扫A和B串的原因。[/b][/b]
[b]补充完这段,接着开始:比如我们正在扫A串,那么当B串入栈时,sum均会加上L-K+1,而之后当有height值小的要入栈,此时可以是A串的后缀,他虽然不增加sum的值,但它也起到了阻断作用,它是之前栈里height值>=它的值变废了,因为后面的后缀与栈里算LCP值时,起码要<=这个值,由于栈里原先的元素都加了L-K+1,假设现在入栈的height值为T(T<L),那么它对答案的贡献是T-K+1,它使原先栈里大于等于它废成T,贡献量变为T-K+1,相当于减少了[b]L-K+1-([b]T-K+1)=L-K(高度差),因为之前入栈时已加在sum上,此时就要从sum中减去了,当然不能忘了乘宽度,因为这些height都会合并,他代表的是一系列height相同的元素,是有数目的。[/b][/b][/b]
开始用比较习惯的写法,提交过了发现跑了900多,感觉有些慢,换了以前的写法又写了一遍稍微快一点点
ac代码1
ac代码2
Time Limit: 5000MS | Memory Limit: 65536K | |
Total Submissions: 8471 | Accepted: 2798 |
A substring of a string T is defined as:
T(i, k)=TiTi+1...Ti+k-1, 1≤i≤i+k-1≤|T|.
Given two strings A, B and one integer K, we define
S, a set of triples (i, j, k):
S = {(i, j, k) | k≥K,
A(i, k)=B(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
Source
POJ Monthly--2007.10.06, wintokk
题目大意:
就是可以匹配到的长度大于k的公共子串个数,即S = {(i, j, k) |
k≥K, A(i, k)=B(j,
k)}.个数
例如第二组,长度为1时,第一个串取第一个x,第二个串可以去第一个或第二个,种类为2,第一个串取第二个一样,种类又是2,对于长度为2时,第一个串全取,第二个串全取,一种,总共有五种
思路:按题目的意思可以转换为求所有的i ,j,两个串后缀的最长公共前缀长度lcp(s1[i],s2[j])-k+1>0的和,暴力绝对超时,所以运用一个单调栈来求所有串和这个串的最长前缀
剩下的思路讲解来自http://m.blog.csdn.net/blog/u012866104/38725689
[b]这道题需要对height数组分组后,用单调栈优化。对于LCP=L>K的前缀,对答案的贡献是L-K+1.即长度为K,K+1.....L的公共字串。对于每一组,栈里维护height值递增,这样保证了每个height的贡献量为height[i]-K+1,因为有定理LCP(i,j)=min(height[i+1],...height[j])如果在height[i]之后插入一个height[k]<height[i],那么height[i]就等于废了,他的价值仅为height[k]了,对于之后的j,LCP(i,j)起码要<=height[k],像这种递减的值插入栈前,需要将栈里>=插入值的变成插入值,然后将插入值的宽度增加。因为这当中为了降低复杂度,用了一个sum来维护当前栈里的后缀与将要入栈的后缀的公共字串的个数。因此实际上的{[b]将栈里>=插入值的变成插入值}的操作,体现在sum上,是对sum减去一段height之差*宽度。[/b][/b]
[b][b]补充:因为rank=i的后缀能与其rank前后的后缀都可能产生公共子串,但此算法只维护其前面的,因此排在A串后缀后面的B串后缀,应该用扫描B串后缀来维护,因为B在A后,以B为参考系,A就在前,这就是为什么要分别扫A和B串的原因。[/b][/b]
[b]补充完这段,接着开始:比如我们正在扫A串,那么当B串入栈时,sum均会加上L-K+1,而之后当有height值小的要入栈,此时可以是A串的后缀,他虽然不增加sum的值,但它也起到了阻断作用,它是之前栈里height值>=它的值变废了,因为后面的后缀与栈里算LCP值时,起码要<=这个值,由于栈里原先的元素都加了L-K+1,假设现在入栈的height值为T(T<L),那么它对答案的贡献是T-K+1,它使原先栈里大于等于它废成T,贡献量变为T-K+1,相当于减少了[b]L-K+1-([b]T-K+1)=L-K(高度差),因为之前入栈时已加在sum上,此时就要从sum中减去了,当然不能忘了乘宽度,因为这些height都会合并,他代表的是一系列height相同的元素,是有数目的。[/b][/b][/b]
开始用比较习惯的写法,提交过了发现跑了900多,感觉有些慢,换了以前的写法又写了一遍稍微快一点点
ac代码1
Problem: 3415 User: kxh1995 Memory: 5064K Time: 938MS Language: C++ Result: Accepted
#include<stdio.h> #include<string.h> #include<algorithm> #include<iostream> using namespace std; char str[200010]; int sa[200100],Rank[200100],rank2[200100],height[200010],c[200100],*x,*y; int k,stack[200100][2]; int n; void cmp(int n,int sz) { int i; memset(c,0,sizeof(c)); for(i=0;i<n;i++) c[x[y[i]]]++; for(i=1;i<sz;i++) c[i]+=c[i-1]; for(i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i]; } void build_sa(char *s,int n,int sz) { x=Rank,y=rank2; int i,j; for(i=0;i<n;i++) x[i]=s[i],y[i]=i; cmp(n,sz); int len; for(len=1;len<n;len<<=1) { int yid=0; for(i=n-len;i<n;i++) { y[yid++]=i; } for(i=0;i<n;i++) if(sa[i]>=len) y[yid++]=sa[i]-len; cmp(n,sz); swap(x,y); x[sa[0]]=yid=0; for(i=1;i<n;i++) { if(y[sa[i-1]]==y[sa[i]]&&sa[i-1]+len<n&&sa[i]+len<n&&y[sa[i-1]+len]==y[sa[i]+len]) x[sa[i]]=yid; else x[sa[i]]=++yid; } sz=yid+1; if(sz>=n) break; } for(i=0;i<n;i++) Rank[i]=x[i]; } void getHeight(char *s,int n) { int k=0; for(int i=0;i<n;i++) { if(Rank[i]==0) continue; k=max(0,k-1); int j=sa[Rank[i]-1]; while(s[i+k]==s[j+k]) k++; height[Rank[i]]=k; } } int main() { while(scanf("%d",&k)!=EOF,k) { int i; scanf("%s",str); int len=strlen(str); str[len]=127; scanf("%s",str+len+1); int n=strlen(str); build_sa(str,n+1,128); getHeight(str,n); __int64 tot,top; tot=top=0; __int64 sum=0; for(i=1;i<=n;i++) { if(height[i]<k) top=tot=0; else { int cnt=0; if(sa[i-1]<len) { cnt++; tot+=height[i]-k+1; } while(top>0&&height[i]<=stack[top-1][0]) { top--; tot-=stack[top][1]*(stack[top][0]-height[i]); cnt+=stack[top][1]; } stack[top][0]=height[i]; stack[top++][1]=cnt; if(sa[i]>len) sum+=tot; } } tot=top=0; for(i=1;i<=n;i++) { if(height[i]<k) top=tot=0; else { int cnt=0; if(sa[i-1]>len) { cnt++; tot+=height[i]-k+1; } while(top>0&&height[i]<=stack[top-1][0]) { top--; tot-=stack[top][1]*(stack[top][0]-height[i]); cnt+=stack[top][1]; } stack[top][0]=height[i]; stack[top++][1]=cnt; if(sa[i]<len) sum+=tot; } } printf("%I64d\n",sum); } }
ac代码2
Problem: 3415 User: kxh1995 Memory: 5856K Time: 797MS Language: C++ Result: Accepted
#include<stdio.h> #include<string.h> #include<algorithm> #include<iostream> #define min(a,b) (a>b?b:a) #define max(a,b) (a>b?a:b) using namespace std; char str[200010]; int sa[200100],t1[200200],t2[200200],c[200200]; int rank[200200],height[200200]; int k,stack[200100][2]; int n; void build_sa(char s[],int n,int m) { int i,j,p,*x=t1,*y=t2; for(i=0;i<m;i++) c[i]=0; for(i=0;i<n;i++) c[x[i]=s[i]]++; for(i=1;i<m;i++) c[i]+=c[i-1]; for(i=n-1;i>=0;i--) sa[--c[x[i]]]=i; for(j=1;j<=n;j<<=1) { p=0; for(i=n-j;i<n;i++) y[p++]=i; for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j; for(i=0;i<m;i++) c[i]=0; for(i=0;i<n;i++) c[x[y[i]]]++; for(i=1;i<m;i++) c[i]+=c[i-1]; for(i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i]; swap(x,y); p=1; x[sa[0]]=0; for(i=1;i<n;i++) x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+j]==y[sa[i]+j]?p-1:p++; if(p>=n) break; m=p; } } void getHeight(char *s,int n) { int i,j,k=0; for(i=0;i<=n;i++) rank[sa[i]]=i; for(i=0;i<n;i++) { if(k) k--; j=sa[rank[i]-1]; while(s[i+k]==s[j+k]) k++; height[rank[i]]=k; } } int main() { while(scanf("%d",&k)!=EOF,k) { int i; scanf("%s",str); int len=strlen(str); str[len]=127; scanf("%s",str+len+1); int n=strlen(str); build_sa(str,n+1,128); getHeight(str,n); __int64 tot,top; tot=top=0; __int64 sum=0; for(i=1;i<=n;i++) { if(height[i]<k) top=tot=0; else { int cnt=0; if(sa[i-1]<len) { cnt++; tot+=height[i]-k+1; } while(top>0&&height[i]<=stack[top-1][0]) { top--; tot-=stack[top][1]*(stack[top][0]-height[i]); cnt+=stack[top][1]; } stack[top][0]=height[i]; stack[top++][1]=cnt; if(sa[i]>len) sum+=tot; } } tot=top=0; for(i=1;i<=n;i++) { if(height[i]<k) top=tot=0; else { int cnt=0; if(sa[i-1]>len) { cnt++; tot+=height[i]-k+1; } while(top>0&&height[i]<=stack[top-1][0]) { top--; tot-=stack[top][1]*(stack[top][0]-height[i]); cnt+=stack[top][1]; } stack[top][0]=height[i]; stack[top++][1]=cnt; if(sa[i]<len) sum+=tot; } } printf("%I64d\n",sum); } }
相关文章推荐
- 代理模式和装饰模式
- 时间四象限
- 数据库大型应用解决方案总结
- VC中的0xcccccccc与0x00000000
- C hicken and rabbit s
- java 3种简单的排序
- ios开发技巧(二)
- 欢迎使用CSDN-markdown编辑器
- LeetCode----Invert Binary Tree
- IOS项目开发--过隙
- hdu 1198 Farm Irrigation 搜索/并查集
- 二叉树操作练习
- 再谈static
- 题目1111:单词替换
- SolrJ 操作HttpSolrServer, ConcurrentUpdateSolrServer和CloudSolrServer
- 脱坑之路
- Java基础笔记-反射及应用
- Android最常用的ListView解决方案大集合
- 一个池塘里漂浮着一只装有石块的木船,当把石块扔到池塘后,石块下沉,问池塘中的水面高度将如何变化?
- Emacs 学习(一)