给定一个1到10 ^ 5之间的整数数组,可以在最佳时间和空间中找到最长的重复子数组的长度。 我当时在考虑进行二进制搜索,但我想听听一些建议。感谢您的帮助!
答案 0 :(得分:1)
可以使用独立的二进制搜索,但是需要对数组进行哈希处理才能使整体算法高效,您可以阅读有关滚动哈希的更多信息,滚动哈希就像您为子数组创建哈希一样,因此如果要检查两个子数组是否相等,那么您可以在O(1)时间内检查它们的滚动哈希,如果它们相等,则非常高的可能性是它们相等,具体取决于您的哈希函数,因此您将对所需的len进行二进制搜索重复子数组,即假设长度范围是从0到(n / 2),其中n是数组的大小,0表示不存在重复子数组,因此假设我们以中位数作为潜在答案,请创建一个check函数,其中制作一个哈希图,其中整数是键,值是向量,用于存储长度为mid的子数组的所有起始位置的哈希值
unordered_map<int , vector<int>>pos;
现在遍历数组并将所有散列存储为键,并将它们的开始位置存储在向量中,因此,如果重复两个散列,它将进入同一向量, 现在,一旦完成,我们就获得了最多n个不同的哈希,因此遍历map中的哈希,并且如果vector的大小大于1,则检查该对应哈希的向量的第一个元素和最后一个元素的pos之间的差,如果差是> = len(或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;
}
现在用于计算子数组/子字符串哈希,您可以参考Internet上的滚动哈希,让我知道是否不清楚。