-
题目描述:
-
给定两个字符串,求它们前后重叠的最长子串的长度,比如"abcde"和“cdefg”是"cde",长度为3。
-
输入:
-
输入可能包含多个测试案例。
对于每个测试案例只有一行, 包含两个字符串。字符串长度不超过1000000,仅包含字符'a'-'z'。
-
输出:
-
对应每个测试案例,输出它们前后重叠的最长子串的长度。
-
样例输入:
-
abcde cdefg
- #include <iostream>
- #include <cstring>
- #include <cstddef>
- #include <cstdio>
- using namespace std;
- const int MAXN = 1000001;
- int next[MAXN];
- int extend[MAXN];
- char S[MAXN],T[MAXN];
- void getnext(char *T)
- {
- int k = 0;
- int Tlen = strlen(T);
- next[0] = Tlen;
- while(k < Tlen-1 && T[k] == T[k+1])
- k++;
- next[1] = k;
- k = 1;
- for(int i = 2; i < Tlen; i++)
- {
- int p = k+next[k]-1, L = next[i-k]; // p :=已经匹配到的最远的位置
- // L :=
- if((i-1)+L >= p)
- {
- int j=(p-i+1)>0? p-i+1:0;
- while(i+j < Tlen && T[k+j]==T[j])
- j++;
- next[i] = j;
- k = i;
- }
- else next[k]=L;
- }
- }
- void getextend(char *S,char *T)
- {
- int a = 0;
- getnext(T);
- int Slen = strlen(S);
- int Tlen = strlen(T);
- int len = Slen<Tlen ? Slen:Tlen;
- while(a < len && S[a] == T[a]) a++;
- extend[0] = a;
- a = 0;
- for(int i = 1; i < Slen; i++)
- {
- int p = a + extend[a]-1, L = next[i-a];
- if(i+L-1 >= p)
- {
- int j = (p-i+1)>0? p-i+1:0;
- while(i+j < Slen && j<Tlen && S[i+j] == T[j])
- j++;
- extend[i] = j;
- a = i;
- }
- else extend[i] = L;
- }
- }
- int main()
- {
- while(scanf("%s%s",&S,&T) != EOF)
- {
- getextend(S,T);
- int n = strlen(S);
- int res = 0;
- for(int i = 0; i < n; i++)
- {
- if(extend[i] == n-i)
- {
- res = extend[i];
- break;
- }
- }
- cout<<res<<endl;
- }
- return 0;
- }
- 下面介绍一种滚动哈希算法:假设要求S的后缀和T的前缀相等的最大长度,也可以利用滚动哈希在O(n+m)的时间内求解:
- 假设字符串C = c1c2....cm 定义哈希函数:
- H(C) = (c1*b^(m-1)+c2*b^(m-2)+.....+cm*b^(0)) mod h ,
- 其中 b和h是两个互素的数,这样,我们就可以将字符串 S=s1s2...sn 从位置 k+1 开始长度为m的 子串 S[k+1....k+m]的哈希值
- 根据 子串 S[k....k+m-1]的哈希值在常数
- 时间内求出来:
- H(S[k+1....k+m]) = H(S[k..k+m-1]*b - sk*b^m +s(k+m)) mod h ,
- 只要不断这样右移,就可以在O(n)的时间内求得所有位置的哈希值
- <pre name="code" class="cpp">#include <iostream>
- #include <string>
- #include <cstddef>
- #include <cstdlib>
- using namespace std;
- typedef unsigned long long ull;
- const ull B = 100000007;
- int overlap(string &a,string &b)
- {
- int alen = a.size(), blen = b.size();
- int res = 0;
- ull ahash = 0, bhash = 0, t = 1;
- for(int i = 1; i <= min(alen,blen); i++)
- {
- ahash = ahash + (a[alen-i]-'a')*t; //a的长度为i的后缀哈希值
- bhash = bhash*B + b[i-1]-'a'; //b的长度为i的前缀哈希值
- if(ahash == bhash) res = i;
- t *= B;
- }
- return res;
- }
- int main()
- {
- string a,b;
- while(cin>>a>>b)
- {
- cout<<overlap(a,b)<<endl;
- }
- return 0;
- }</pre>顺便说一下:不同字符串的哈希值发生冲突的概率非常低,通常可以忽略朴素的检查。
样例输出:
3
思路:扩展KMP,用extend[i]保存 主串 S[i.....n-1]与 模式串 T的最长公共前缀的长度,其中n是S的长度。
然后扫描一遍 extend[] 如 extend[i] == n-i 那么这个后缀的长度就是我们要求的值。
关于扩展KMP,可以去看论文:《求最长回文子串与最长重复子串》何林 的集训队论文
代码: