子数组之性质篇「或|与|GCD|乘法」

2411. 按位或最大的最小子数组长度

898. 子数组按位或操作

1521. 找到最接近目标值的函数值

6224. 最大公因数等于 K 的子数组数目

D. CGCDSSQ

题目 2622: 蓝桥杯 2021 年第十二届国赛真题-和与乘积

 

本篇文章要介绍的是一个利用某些性质的子数组模版,可利用的性质有:或运算性质与运算性质GCD 性质乘法性质

该模版可以做到:

下面从题目依次阐述上面说到的内容!!

按位或最大的最小子数组长度

题目详情可见 按位或最大的最小子数组长度

这个题目还总结了另外一种方法,详情可见 子数组之「与」「或」

「按位或」有一个特点:对于x = x | yx的二进制中要么1还是1,要么0变成1,也就是单调非递减的特性

从最坏的情况出发,假设x = 0,每次改变一个比特,最终得到 2291<109,那么在不断向右扩展的过程中,x的值最多只有 30 中情况

举个小例子:x = 00000x的变化顺序可能是x = 10000; x = 11000; x = 11100; x = 11110; x = 11111;一定不可能出现的变化顺序:x = 10000; x = 01000

下面根据本题的一个例子展开讨论:nums = [1,0,2,1,3] (这里有一个小技巧:从后往前遍历)

10

左图是没有优化的过程,右图是优化了的过程,可以看到我们将同一行相同的元素进行了合并,而且是向左合并

比如第四行:3 [1,3], 3 [1,4]合并成了3 [1,3],为什么不合并成3[1,4]呢??

题目要求「或运算值最大最小非空子数组」,所以[1,3]就能实现的事情,为什么要[1,4]呢!!这也是为什么向左合并了,是因为可以保证子数组长度最小

这里还涉及到一个原地去重的技巧,可见题目 删除有序数组中的重复项。之所以可以原地去重,是因为每一层的结果都有序

下面给出代码的时间复杂度为 O(30×n)

子数组按位或操作

题目详情可见 子数组按位或操作

这个题目和上一个题目几乎一样,不同在于本题只需要求出所有子数组或运算不同结果的数量,我们可以使用Set实现自动去重的效果

找到最接近目标值的函数值

题目详情可见 找到最接近目标值的函数值

「按位与」有一个特点:对于x = x & yx的二进制中要么0还是0,要么1变成0,也就是单调非递增的特性

从最坏的情况出发,假设x = 111...111,每次改变一个比特,最终得到 0,那么在不断向右扩展的过程中,x的值最多只有 30 中情况

举个小例子:x = 11111x的变化顺序可能是x = 11110; x = 11100; x = 11000; x = 10000; x = 00000;一定不可能出现的变化顺序:x = 10000; x = 01000

这个题目,我们可以用一个Set存储所有可能的与运算结果,和上一题一样,然后在所有的结果中寻找与目标值target相差最小的那一个即可

最大公因数等于 K 的子数组数目

题目详情可见 最大公因数等于 K 的子数组数目

先复习一个知识点,求两个数的最大公因数直接gcd(a, b)即可;如果求多个数的最大公因数呢?如:求a, b, c的最大公因数

15

左图是没有优化的过程,中间的图是去除了重复 GCD 的过程,右图是删去了因子中没有 K 的情况,因为没有因子 K,必不可能组成最大公因数等于 K 的子数组,我们用i0记录这种情况的下标

根据 gcd 的特点,每一层的 gcd 值单调非递减,这也是可以原地去重的前提!!

下面给出代码的时间复杂度为 O(n logU), U=max(nums)

D. CGCDSSQ

题目详情可见 D. CGCDSSQ

这个题和上一个题目几乎一模一样,但是有一些处理上的技巧!!如果我们按照上个题的方法,每次一次查询都调用一次函数,那么这样会超时!!

我们可以一次遍历,计算出所有不同最大公因数的结果,存储起来,然后每次查询就可以只用 O(1) 的时间

蓝桥杯 2021 年第十二届国赛真题-和与乘积

题目详情可见 和与乘积

14

类似的,每一层的乘积值单调非递增,这也是可以原地去重的前提!!

需要注意的是,由于 1 乘任何数都等于本身,导致去重合并的时候,需要存储相同值的左右边界

比如:区间[0,2][1,2]的乘积都是 6,但如果只记录右边界1,可能导致可行解a[0] + a[1] + a[2] = a[0] * a[1] * a[2]丢失

所以对于每一个子数组区间,都记录两个值,分别表示左右边界,然后可以利用前缀和具有单调性,二分搜索的去寻找

可以看出「乘积」和「GCD」是一个逆过程,乘积是通过因子求原数的过程,GCD 是通过原数求因子的过程,它们俩每一层的单调性刚好相反!!