重叠的最长子串 (扩展KMP算法)(滚动哈希算法--Rabin-Karp算法)

【九度0J】 题目1535:重叠的最长子串 (扩展KMP算法)(滚动哈希算法--Rabin-Karp算法)

分类: 挑战程序设计竞赛---读书笔记 Algorithm-字符串 枚举   115人阅读  评论(0)  收藏  举报
题目描述:

给定两个字符串,求它们前后重叠的最长子串的长度,比如"abcde"和“cdefg”是"cde",长度为3。

输入:

输入可能包含多个测试案例。
对于每个测试案例只有一行, 包含两个字符串。字符串长度不超过1000000,仅包含字符'a'-'z'。

输出:

对应每个测试案例,输出它们前后重叠的最长子串的长度。

样例输入:
abcde cdefg

样例输出:

3

思路:扩展KMP,用extend[i]保存 主串 S[i.....n-1]与 模式串 T的最长公共前缀的长度,其中n是S的长度。

然后扫描一遍 extend[] 如 extend[i] == n-i 那么这个后缀的长度就是我们要求的值。

关于扩展KMP,可以去看论文:《求最长回文子串与最长重复子串》何林 的集训队论文

代码:

[cpp]  view plain copy
  1. #include <iostream>  
  2. #include <cstring>  
  3. #include <cstddef>  
  4. #include <cstdio>  
  5.   
  6. using namespace std;  
  7. const int MAXN = 1000001;  
  8. int next[MAXN];  
  9. int extend[MAXN];  
  10. char S[MAXN],T[MAXN];  
  11.   
  12. void getnext(char *T)    
  13. {    
  14.     int k = 0;    
  15.     int Tlen = strlen(T);   
  16.     next[0] = Tlen;    
  17.     while(k < Tlen-1 && T[k] == T[k+1])   
  18.         k++;   
  19.     next[1] = k;    
  20.       
  21.     k = 1;  
  22.     for(int i = 2; i < Tlen; i++)    
  23.     {    
  24.         int p = k+next[k]-1, L = next[i-k];   // p :=已经匹配到的最远的位置  
  25.                                             // L :=     
  26.         if((i-1)+L >= p)    
  27.         {    
  28.             int j=(p-i+1)>0? p-i+1:0;    
  29.             while(i+j < Tlen && T[k+j]==T[j])   
  30.                 j++;    
  31.             next[i] = j;    
  32.             k = i;    
  33.         }    
  34.         else next[k]=L;    
  35.     }    
  36. }    
  37.     
  38. void getextend(char *S,char *T)    
  39. {    
  40.     int a = 0;    
  41.     getnext(T);    
  42.     int Slen = strlen(S);    
  43.     int Tlen = strlen(T);    
  44.     int len = Slen<Tlen ? Slen:Tlen;    
  45.     while(a < len && S[a] == T[a]) a++;    
  46.     extend[0] = a;    
  47.     a = 0;    
  48.     for(int i = 1; i < Slen; i++)    
  49.     {    
  50.         int p = a + extend[a]-1, L = next[i-a];    
  51.           
  52.         if(i+L-1 >= p)  
  53.         {    
  54.             int j = (p-i+1)>0? p-i+1:0;    
  55.               
  56.             while(i+j < Slen && j<Tlen && S[i+j] == T[j])   
  57.                 j++;    
  58.             extend[i] = j;    
  59.             a = i;    
  60.         }    
  61.         else extend[i] = L;    
  62.     }    
  63. }  
  64.   
  65. int main()  
  66. {  
  67.     while(scanf("%s%s",&S,&T) != EOF)  
  68.     {  
  69.         getextend(S,T);  
  70.         int n = strlen(S);  
  71.         int res = 0;  
  72.         for(int i = 0; i < n; i++)  
  73.         {  
  74.             if(extend[i] == n-i)  
  75.             {  
  76.                 res = extend[i];  
  77.                 break;  
  78.             }  
  79.         }  
  80.         cout<<res<<endl;  
  81.     }  
  82.     return 0;  
  83. }  
[cpp]  view plain copy
  1.  下面介绍一种滚动哈希算法:假设要求S的后缀和T的前缀相等的最大长度,也可以利用滚动哈希在O(n+m)的时间内求解:  
  2.  假设字符串C = c1c2....cm 定义哈希函数:   
  3.  H(C) = (c1*b^(m-1)+c2*b^(m-2)+.....+cm*b^(0)) mod h  ,     
  4.   其中 b和h是两个互素的数,这样,我们就可以将字符串 S=s1s2...sn 从位置 k+1 开始长度为m的 子串 S[k+1....k+m]的哈希值   
  5.   根据 子串 S[k....k+m-1]的哈希值在常数  
  6.  时间内求出来:   
  7.   H(S[k+1....k+m]) = H(S[k..k+m-1]*b - sk*b^m +s(k+m)) mod h ,  
  8. 只要不断这样右移,就可以在O(n)的时间内求得所有位置的哈希值  
[cpp]  view plain copy
  1. <pre name="code" class="cpp">#include <iostream>  
  2. #include <string>  
  3. #include <cstddef>  
  4. #include <cstdlib>  
  5. using namespace std;  
  6. typedef unsigned long long ull;  
  7. const ull B = 100000007;  
  8.   
  9. int overlap(string &a,string &b)  
  10. {  
  11.     int alen = a.size(), blen = b.size();  
  12.     int res = 0;  
  13.     ull ahash = 0, bhash = 0, t = 1;  
  14.     for(int i = 1; i <= min(alen,blen); i++)  
  15.     {  
  16.         ahash = ahash + (a[alen-i]-'a')*t; //a的长度为i的后缀哈希值  
  17.         bhash = bhash*B + b[i-1]-'a';     //b的长度为i的前缀哈希值  
  18.         if(ahash == bhash) res = i;  
  19.         t *= B;   
  20.     }  
  21.     return res;  
  22. }   
  23.   
  24. int main()  
  25. {  
  26.     string a,b;  
  27.     while(cin>>a>>b)  
  28.     {  
  29.         cout<<overlap(a,b)<<endl;  
  30.     }  
  31.     return 0;  
  32. }</pre>顺便说一下:不同字符串的哈希值发生冲突的概率非常低,通常可以忽略朴素的检查。
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值