Algorithm 查找具有目标位和值的子阵列
给定一个大小为Algorithm 查找具有目标位和值的子阵列,algorithm,data-structures,Algorithm,Data Structures,给定一个大小为N的数组A和一个整数p,找到子数组B=A[i…j],这样i您熟悉这个数组吗?我提议的解决方案使用了与链接中的高效解决方案相同的方法。强烈建议在继续之前阅读它 首先,让我们注意子数组itsK越长,它将越小,因为两个数字之间的&运算符只能创建较小的数字 因此,如果我有一个从I到j的子数组,我想把它的K变小,我会添加更多的元素(现在子数组是从I到j+1),如果我想把K变大,我会删除元素(I+1到j) 如果我们回顾一下查找具有给定和的子数组的解决方案,我们会发现我们可以很容易地将其转换为我
N
的数组A
和一个整数p
,找到子数组B=A[i…j]
,这样i您熟悉这个数组吗?我提议的解决方案使用了与链接中的高效解决方案相同的方法。强烈建议在继续之前阅读它
首先,让我们注意子数组itsK
越长,它将越小,因为两个数字之间的&
运算符只能创建较小的数字
因此,如果我有一个从I
到j
的子数组,我想把它的K
变小,我会添加更多的元素(现在子数组是从I
到j+1
),如果我想把K
变大,我会删除元素(I+1
到j
)
如果我们回顾一下查找具有给定和的子数组的解决方案,我们会发现我们可以很容易地将其转换为我们的问题-给定和是K
,求和就像使用&
运算符,但是更多的元素更小K
,因此我们可以翻转和的比较
这个问题告诉你解决方案是否存在,但是如果你只是保持你目前发现的最小差异,你也可以解决你的问题
编辑
如评论中所述,如果所有数字均为正值,则此解决方案为真;如果并非所有数字均为正值,则解决方案略有不同
<>注意,如果不是所有的数字都是否定的,则<代码> k>代码>将是肯定的,所以为了找到否定的<代码> p>代码>我们可以只考虑算法中的否定,而不是使用上面所示的算法。 答案是错误的。p>
在解决方案中,我们有一个循环,在那里我们做子结构
while (curr_sum > sum && start < i-1)
curr_sum = curr_sum - arr[start++];
<>强>这里是C++中的代码> o(n^ 2)< />代码解决方案(感谢解答)< /强>的想法是更新当前总和,而不是调用代码< > GATSUM < /COD>每两个索引。你们也应该看看答案,因为它包含了早期布拉克的条件。这里是C++版本:
int maxSubArray(const std::vector<int>& vec, int target) {
auto minDiff = UINT_MAX;
for (auto i = 0; i < vec.size(); i++) {
unsigned int sum = -1;
for (auto j = i; j < vec.size(); j++) {
sum &= (unsigned int)vec[j];
updateMin(minDiff, sum, target);
}
}
return minDiff;
}
这个解决方案的问题是我们跳过了一些实际上可能是最好的序列
输入:vector=[26,77,21,6]
,target=5
输出应为零,为77和21=5,但滑动窗口方法不能找到它,因为它将首先考虑窗口[0…3 ],而不是增加下界,不可能考虑窗口[1…2 ]。
如果有人有一个线性或对数线性的解决方案,这将是很好的张贴 这里是一种准线性方法,假设数组的元素具有恒定的位数
矩阵的行K[i,j]=A[i]&A[i+1]&…&A[j]
是单调递减的(忽略矩阵的下三角)。这意味着K[i,:]
和搜索参数P
之间的差值的绝对值是单峰的,并且可以在O(logn)时间内用(假设对K
元素的访问可以在固定时间内安排)。对每行重复此操作,并输出最低最小值的位置,使其达到O(n log n)
在小于行大小的时间内执行行最小搜索需要隐式访问矩阵K
的元素,这可以通过创建b
前缀和数组来实现,每个前缀和数组对应a
.a范围的元素位,然后可以通过计算所有b
单个位范围来找到e-求和并将其与范围长度进行比较,每次比较给出范围的单个位和。这需要O(nb)预处理,并给出O(b)(如此恒定,根据我在开始时所做的假设)访问K
的任意元素
我曾希望绝对差分矩阵是一个Monge矩阵,允许使用SMAWK算法,但事实似乎并非如此,我无法找到一种方法来实现这一特性。这里是另一个准线性算法,将查找子数组与给定的求和问题解决方案与idea to compute<代码混合在一起>K[i,j]
;因此,如果内存不足,我不使用预处理。我使用计数器跟踪位,最多计算K
的2N
值,每个值最多花费O(logn)
。由于logn
通常小于字长(B
),因此比线性O(NB)快
算法
N
数字的位计数只能用~log N
字完成:
因此,您只需执行logn
操作即可计算A[i]&A[i+1]&…&A[i+N-1]
下面是管理计数器的方法:如果
计数器
是C0、C1、…Cp
和
Ck
是Ck0,Ck1,…Ckm
然后,Cpq…C1q,C0q
是{A[i],A[i+1],…,A[j-1]}
的第q位中等于1的位数的二进制表示
位级实现(在python中);所有位都是并行管理的
def add(counter,x):
k = 0
while x :
x, counter[k] = x & counter[k], x ^ counter[k]
k += 1
def sub(counter,x):
k = 0
while x :
x, counter[k] = x & ~counter[k], x ^ counter[k]
k += 1
def val(counter,count): # return A[i] & .... & A[j-1] if count = j-i.
k = 0
res = -1
while count:
if count %2 > 0 : res &= counter[k]
else: res &= ~counter[k]
count //= 2
k += 1
return res
算法是:
def solve(A,P):
counter = np.zeros(32, np.int64) # up to 4Go
n = A.size
i = j = 0
K=P # trig fill buffer
mini = np.int64(2**63-1)
while i<n :
if K<P or j == n : # dump buffer
sub(counter,A[i])
i += 1
else: # fill buffer
add(counter,A[j])
j += 1
if j>i:
K = val(counter, count)
X = np.abs(K - P)
if mini > X: mini = X
else : K = P # reset K
return mini
numba编译版本(用@numba.jit
修饰4个函数)快200倍(5毫秒)。这是我编写的一个解决方案,它的时间复杂度为O(n^2)
。
下面的代码段是用Java编写的
class Solution{
public int solve(int[] arr,int p){
int maxk = Integer.MIN_VALUE;
int mink = Integer.MAX_VALUE;
int size = arr.length;
for(int i =0;i<size;i++){
int temp = arr[i];
for(int j = i;j<size;j++){
temp &=arr[j];
if(temp<=p){
if(temp>maxk)
maxk = temp;
}
else{
if(temp < mink)
mink = temp;
}
}
}
int min1 = Math.abs(mink -p);
int min2 = Math.abs(maxk -p);
return ( min1 < min2 ) ? min1 : min2;
}
}
类解决方案{
公共整数解算(int[]arr,int p){
int maxk=整数.MIN_值;
int mink=Integer.MAX_值;
int size=arr.length;
for(int i=0;iIt未说明数组中的元素是正数。逻辑and和负数可以生成更大的数。@Dejan,您可以通过onl找到最近的负数
def solve(A,P):
counter = np.zeros(32, np.int64) # up to 4Go
n = A.size
i = j = 0
K=P # trig fill buffer
mini = np.int64(2**63-1)
while i<n :
if K<P or j == n : # dump buffer
sub(counter,A[i])
i += 1
else: # fill buffer
add(counter,A[j])
j += 1
if j>i:
K = val(counter, count)
X = np.abs(K - P)
if mini > X: mini = X
else : K = P # reset K
return mini
n = 10**5
A = np.random.randint(0, 10**8, n, dtype=np.int64)
P = np.random.randint(0, 10**8, dtype=np.int64)
%time solve(A,P)
Wall time: 0.8 s
Out: 452613036735
class Solution{
public int solve(int[] arr,int p){
int maxk = Integer.MIN_VALUE;
int mink = Integer.MAX_VALUE;
int size = arr.length;
for(int i =0;i<size;i++){
int temp = arr[i];
for(int j = i;j<size;j++){
temp &=arr[j];
if(temp<=p){
if(temp>maxk)
maxk = temp;
}
else{
if(temp < mink)
mink = temp;
}
}
}
int min1 = Math.abs(mink -p);
int min2 = Math.abs(maxk -p);
return ( min1 < min2 ) ? min1 : min2;
}
}