【字符串哈希+二分】AcWing 3508. 最长公共子串
题解
首先思考暴力枚举长度为 \(len∈[1, min(strlen(s), strlen(t))]\),最差情况下为字符串 \(s\) 和字符串 \(t\) 全为长度为 \(10000\) 的全英文字符串,时间复杂度: \(O(n^2)\),显然会超时。
容易证明的是:若存在一个长度为 \(x\) 的公共子串,那么一定能在字符串 \(s\) 和字符串 \(t\) 中找到长度为 \(len∈[1, x - 1]\) 的公共子串。
因此,不妨假设最长的公共子串的长度为 \(x\),我们只需要证明不存在长度为 \(x + 1\) 的公共子串即可。该过程枚举长度 \(x\),满足二分性质。
判断字符串 \(s\) 和字符串 \(t\) 是否存在长度为 \(x\) 的公共子串,可以用字符串哈希来达到快速查找。
参考代码
#include<iostream>
#include<vector>
#include<unordered_set>
#define IOS ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
using namespace std;
using ull = unsigned long long;
const int N = 10007;
const int P = 131;
ull p[N];
vector<vector<ull>> h1, h2;
string s, t;
int n, m;
bool judge(char &ch) {
return ch >= 'a' && ch <= 'z';
}
void init() {
n = s.size(), m = t.size();
p[0] = 1;
for (int i = 1, j = max(n, m); i <= j; ++ i) p[i] = p[i - 1] * P;
auto func = [&](string& str, vector<vector<ull>>& h, int &len) {
for (int i = 0; i < len; ++ i) {
if (judge(str[i])) {
vector<ull> v = {0ULL};
int j = i;
do {
v.emplace_back(v.back() * P + str[i] - 'a');
++ i;
} while (i < len && judge(str[i]));
h.emplace_back(v);
}
}
};
func(s, h1, n), func(t, h2, m);
}
ull get(vector<ull>& vec, int l, int r) {
return vec[r] - vec[l] * p[r - l];
}
int main() {
IOS
cin >> s >> t;
init();
auto check = [&](int x) -> bool {
unordered_set<ull> ust;
for (auto &h: h1) for (int i = x; i < h.size(); ++ i) ust.insert(get(h, i - x, i));
for (auto &h: h2) {
for (int i = x; i < h.size(); ++ i) {
ull val = get(h, i - x, i);
if (ust.find(val) != ust.end()) return true;
}
}
return false;
};
int le = 0, ri = min(n, m), md;
while (le < ri) {
md = le + ri + 1 >> 1;
if (check(md)) le = md;
else ri = md - 1;
}
cout << le << '\n';
return 0;
}
浙公网安备 33010602011771号