【问题标题】:Find the length of the longest repeated subArray查找最长重复子数组的长度
【发布时间】:2020-03-22 08:47:38
【问题描述】:

给定一个 1 到 10^5 之间的整数数组,在最佳时间和空间中找到最长重复子数组的长度。 我正在考虑进行二进制搜索,但我想听听一些建议。感谢您的帮助!

【问题讨论】:

  • “最长重复子数组”是什么意思?你能举个小例子吗?
  • 是后缀数组中最长的公共前缀,或者是后缀树中最深的内部节点。 “后缀数组”和“后缀树”是神奇的谷歌词
  • 是的,比如数组是{0,2,4,5,1,2,3,0,2,4,5,2,4}最长重复子数组是{0, 2,4,5}

标签: algorithm sub-array array-algorithms


【解决方案1】:

确实可以使用二进制搜索,但它需要对数组进行散列以使整体算法高效,您可以阅读更多关于滚动哈希的信息,滚动哈希就像您为子数组创建哈希一样,所以如果您想检查两个子数组是否相等,那么您可以在 O(1) 时间内检查它们的滚动哈希,如果它们相等,则它们很有可能是相等的,具体取决于您的哈希函数,因此您将对所需的 len 进行二进制搜索重复子数组,即假设长度范围是从 0 到 (n/2),其中 n 是数组的大小,0 表示不存在重复子数组,因此假设我们有 mid 作为潜在答案,请创建一个检查函数,其中现在制作一个哈希图,其中整数是键,值作为向量,它存储长度为 mid 的子数组的所有起始位置的哈希

unordered_map<int , vector<int>>pos;

现在遍历数组并将所有散列存储为键并将它们的起始位置存储在向量中,因此如果重复两个散列,它将进入同一个向量, 现在完成后,我们得到了最大 n 个不同的哈希值,因此遍历 map 中的哈希值,如果向量的大小大于 1,则检查该向量的第一个和最后一个元素的 pos 之间的差异,以获取相应的哈希值,如果差异是 >= len(or mid) 那么是的,你有一个长度为 mid 的子数组并将其存储在我们的答案中,这是重复的,现在二进制搜索的魔力来了,我们可以很容易地证明,如果这个子数组/子字符串是重复的然后它的任何子数组/子字符串也在重复,因此基于这种模式,我们尝试寻找更高的 len,这可能是潜在的答案,即我们更新 l = mid + 1,假设现在我们得到的 mid 没有't 不是有效的 len,因此可以确定不会存在长度大于或等于该长度的重复子数组,因此我们选择稍低的范围,即 r = mid - 1,直到我们完成了我们的二分搜索,它将具有最大 log(n/2) 迭代,并且每个检查函数将在每个 i 中进行 n 次迭代二分搜索的迭代因此该算法的总复杂度(假设您正在使用散列并获取可以在 O(1) 中检索的子字符串/子数组散列,这实际上可以通过首先对原始数组进行一些预处理并制作一个具有散列的新数组我们可以获得子数组哈希的值是 n * log(n/2) => O(n*log(n)) 下面是c++中的粗略代码,便于理解

#include<iostream>
#include<unordered_map>
#include<vector>
using namespace std;
bool check(vector<int> & a , int len){

    int n = a.size();
    unordered_map<int , vector<int>> pos;

    for(int i = 0; i < n - len + 1; ++i){
        int hash_value = subarray_hash(a , i , i + len - 1); // some function to get subarray hash, which I have not implementated for OP exercise
        pos[hash_value].push_back(i);
    }
    for(auto it = pos.begin(); it != pos.end(); ++it){
        vector<int> all_pos = *it;
        if(all_pos.size() > 1){
            int k = all_pos.size();
            if(all_pos[k - 1] - all_pos[0] >= len){
                return true;
            }
        }
    }
    return false;
}
int main(){

    int n;
    cin >> n;
    vector<int>a(n);
    for(int i = 0; i < n; ++i){
        cin >> a[i];
    }
    int maxlen_possible = 0;
    int l = 0 , r = (n/2);
    while(l <= r){
        int mid = (l + (r - l)/2);
        if(check(a , mid)){
            maxlen_possible = mid;
            l = mid + 1;
        }
        else{
            r = mid - 1;
        }
    }
    cout << maxlen_possible << "\n";

return 0;
}

现在计算子数组/子串hash,可以参考网上的rolling hash,有不清楚的地方告诉我。

【讨论】:

    猜你喜欢
    • 2016-08-11
    • 2020-04-03
    • 2020-04-18
    • 1970-01-01
    • 1970-01-01
    • 2013-07-09
    • 2015-03-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多