给定n个数组,求这n个数组的最长公共子串的长度,例如:
paths = [[0,1,2,3,4], [2,3,4], [4,0,1,2,3]]
的最长公共子串为[2,3]
,长度为2。
这类问题可以使用字符串hash来解决,首先是二分最长公共子串的长度。对于每个二分的长度,都去check
是否存在该长度下的解。hash的主要目的是在O(1)时间得到每个数组的每个子串的hash值,这样就可以判断在相同长度下是否存在某个hash值在每个数组中都存在,即包含相同长度的子串。
typedef unsigned long long ULL;
const int N = 100010, P = 13771;
ULL h[N], p[N];
ULL get(int l, int r){
return h[r] - h[l - 1] * p[r - l + 1];
}
class Solution {
public:
vector<vector<int>> paths;
unordered_map<ULL, int> mp, vis;
bool check(int mid){
mp.clear();
for(int k = 0; k < paths.size(); k ++ ){
auto &a = paths[k];
p[0] = 1;
for(int i = 1; i <= a.size(); i ++ ){
h[i] = h[i - 1] * P + a[i - 1];
p[i] = p[i - 1] * P;
}
vis.clear();
for(int i = mid; i <= a.size(); i ++ ){
ULL t = get(i - mid + 1, i);
if(vis.count(t) == 0) {
mp[t] ++ ;
vis[t] = 1;
}
}
}
int cnt = 0;
for(auto &[u, v]: mp) cnt = max(cnt, v);
return cnt == paths.size();
}
int longestCommonSubpath(vector<vector<int>>& _paths) {
paths = _paths;
int l = 0, r = N;
for(auto &path: paths) r = min(r, (int)path.size());
while(l < r){
int mid = l + r + 1 >> 1;
if(check(mid)) l = mid;
else r = mid - 1;
}
return l;
}
};
Note:哈希函数的P可以调节以避免哈希冲突,一般调节顺序为131->1331->13331->133331
。