求n个数组(字符串)的最长公共子串

给定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

上一篇:【读书笔记】格子路径计数LatticePathEnumeration 学一半的笔记


下一篇:【坚持每日一题5.28】1436. 旅行终点站