题目描述
给定两个字符串,求这两个字符串的不包含数字的最长公共子串的长度。
输入格式
共两行,每行一个由小写字母和数字构成的字符串。
输出格式
一个整数,表示给定两个字符串的不包含数字的最长公共子串的长度。
如果不存在满足要求的非空公共子串,则输出 0
。
数据范围
输入字符串的长度均不超过 10000
。
样例
输入样例:
ab123abccff
abcfacb123
输出样例:
3
(二分 + 字符串哈希) O ( n l o g n ) O(nlogn) O(nlogn)
由求最大子串我们可以分析出二段性用二分法,然后我们思考如何判断某子串是否为公共子串;先想朴素做法,将第一个字符串的每一个子串存到hash表中(O(mn),m为二分法枚举的子串长度),而二分法判断为O(logn),总时间复杂度为O(n^2logn);
数据n的最大值为1e4,会tle.
所以我们要想办法优化,我们可以想到用字符串哈希将每个子串映射成数字存到哈希表里(O(n)),二分为O(logn),总时间复杂度为(O(nlogn)),能ac.
C++ 代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_set>
using namespace std;
typedef unsigned long long ULL;
const int P = 131;
const int N = 20010;
int n,m;
ULL h[N],p[N];
ULL get(int l,int r){
return h[r] - h[l - 1] * p[r - l + 1];
}
bool check(int mid){
unordered_set<ULL> s;
for(int i = 1;i + mid - 1 <= n;i ++){
s.insert(get(i,i + mid - 1));
}
for(int i = n + 1;i + mid - 1 <= n + m;i ++){
if(s.count(get(i,i + mid - 1))) return true;
}
return false;
}
int main()
{
char str[N];
scanf("%s",str +1);
n = strlen(str + 1);
scanf("%s",str + n + 1);
m = strlen(str + n + 1);
p[0] = 1;
for(int i = 1;i <= n + m;i ++){
p[i] = p[i - 1] * P;
char c = str[i];
if(isdigit(c)){
if(i <= n) c = '#';
else c = '@';
}
h[i] = h[i - 1] * P + c;
}
int l = 0,r = min(n,m);
while(l < r){
int mid = l + r + 1>> 1;
if(check(mid)) l = mid;
else r = mid - 1;
}
cout << l;
return 0;
}