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您熟悉这个数组吗?我提议的解决方案使用了与链接中的高效解决方案相同的方法。强烈建议在继续之前阅读它

首先,让我们注意子数组its
K
越长,它将越小,因为两个数字之间的
&
运算符只能创建较小的数字

因此,如果我有一个从
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;
    }
}