|
| 1 | +## 题目地址(1521. 找到最接近目标值的函数值) |
| 2 | + |
| 3 | +https://leetcode-cn.com/problems/find-a-value-of-a-mysterious-function-closest-to-target/ |
| 4 | + |
| 5 | +## 题目描述 |
| 6 | + |
| 7 | + |
| 8 | + |
| 9 | +``` |
| 10 | +Winston 构造了一个如上所示的函数 func 。他有一个整数数组 arr 和一个整数 target ,他想找到让 |func(arr, l, r) - target| 最小的 l 和 r 。 |
| 11 | +
|
| 12 | +请你返回 |func(arr, l, r) - target| 的最小值。 |
| 13 | +
|
| 14 | +请注意, func 的输入参数 l 和 r 需要满足 0 <= l, r < arr.length 。 |
| 15 | +
|
| 16 | + |
| 17 | +
|
| 18 | +示例 1: |
| 19 | +
|
| 20 | +输入:arr = [9,12,3,7,15], target = 5 |
| 21 | +输出:2 |
| 22 | +解释:所有可能的 [l,r] 数对包括 [[0,0],[1,1],[2,2],[3,3],[4,4],[0,1],[1,2],[2,3],[3,4],[0,2],[1,3],[2,4],[0,3],[1,4],[0,4]], Winston 得到的相应结果为 [9,12,3,7,15,8,0,3,7,0,0,3,0,0,0] 。最接近 5 的值是 7 和 3,所以最小差值为 2 。 |
| 23 | +示例 2: |
| 24 | +
|
| 25 | +输入:arr = [1000000,1000000,1000000], target = 1 |
| 26 | +输出:999999 |
| 27 | +解释:Winston 输入函数的所有可能 [l,r] 数对得到的函数值都为 1000000 ,所以最小差值为 999999 。 |
| 28 | +示例 3: |
| 29 | +
|
| 30 | +输入:arr = [1,2,4,8,16], target = 0 |
| 31 | +输出:0 |
| 32 | + |
| 33 | +
|
| 34 | +提示: |
| 35 | +
|
| 36 | +1 <= arr.length <= 10^5 |
| 37 | +1 <= arr[i] <= 10^6 |
| 38 | +0 <= target <= 10^7 |
| 39 | +
|
| 40 | +
|
| 41 | +``` |
| 42 | + |
| 43 | +## 前置知识 |
| 44 | + |
| 45 | +- 位运算 |
| 46 | +- 动态规划 |
| 47 | + |
| 48 | +## 公司 |
| 49 | + |
| 50 | +- 暂无 |
| 51 | + |
| 52 | +## 思路 |
| 53 | + |
| 54 | +首先我们要知道一个前提,那就是对于一个数组 arr,对 arr 的子数组 arr[l:r] 进行与操作满足单调性,也就是说 arr[l:r] >= arr[l+1:r] >= arr[l+2:r] ... 。其中的依据是**一个数异或另外一个数的结果一定不会比这两个数大**。 |
| 55 | + |
| 56 | +为了更好的理解本题。我们可以从一个简单的例子入手。 |
| 57 | + |
| 58 | +题目描述: |
| 59 | + |
| 60 | +``` |
| 61 | +如果让你求一个数组的连续子数组总个数,你会如何求?其中连续指的是数组的索引连续。 比如 [1,3,4],其连续子数组有:[1], [3], [4], [1,3], [3,4] , [1,3,4],你需要返回 6。 |
| 62 | +``` |
| 63 | + |
| 64 | +一种思路是总的连续子数组个数等于:以索引为 0 结尾的子数组个数 + 以索引为 1 结尾的子数组个数 + … + 以索引为 n - 1 结尾的子数组个数,这无疑是完备的。 |
| 65 | + |
| 66 | +> 关于这点不熟悉的,也可以看下我的 [【西法带你学算法】一次搞定前缀和](https://lucifer.ren/blog/2020/09/27/atMostK/) |
| 67 | +
|
| 68 | + |
| 69 | + |
| 70 | +我们也可以采用同样的思路进行枚举。 |
| 71 | + |
| 72 | +总的连续子数组按位与操作等于:以索引为 0 结尾的子数组按位与操作的结果, 以索引为 1 结尾的子数组按位与操作的结果 + … + 以索引为 n - 1 结尾的子数组按位与操作的结果,这无疑是完备的。 |
| 73 | + |
| 74 | +而题目我求的不是总和,而是与 target 最接近的,不过这不难,使用一个全局变量记录即可。 |
| 75 | + |
| 76 | +以索引为 i 结尾的子数组按位与操作的结果 sub[i] 其实就等于 `sub[i-1] & A[i]`。这提示我们使用滚动数组来完成。而由于重复数字是没有意义的,因此可使用 hashset 来优化,而不是普通的数组,这样可以同时降低时间和空间复杂度。 |
| 77 | + |
| 78 | +> 关于滚动数组可以参考我之前写的动态规划章节 |
| 79 | +
|
| 80 | +这种解法其实就是暴力求解,和动态规划没有啥区别。 |
| 81 | + |
| 82 | +## 关键点 |
| 83 | + |
| 84 | +- 识别出函数 func 满足某种单调性 |
| 85 | +- 采用合适的枚举方法 |
| 86 | + |
| 87 | +## 代码 |
| 88 | + |
| 89 | +代码支持:Python3 |
| 90 | + |
| 91 | +```python |
| 92 | +class Solution: |
| 93 | + def closestToTarget(self, A: List[int], target: int) -> int: |
| 94 | + seen = set() |
| 95 | + ans = float('inf') |
| 96 | + for a in A: |
| 97 | + seen.add(a) |
| 98 | + t = set() |
| 99 | + # 类似滚动数组 此时的 seen 相当于 sub[i-1] |
| 100 | + for b in seen: |
| 101 | + yu = a & b |
| 102 | + ans = min(ans, abs(yu - target)) |
| 103 | + t.add(yu) |
| 104 | + # 此时的 t 就是 sub[i],我们需要更新回 seen |
| 105 | + seen = t |
| 106 | + return ans |
| 107 | +``` |
| 108 | + |
| 109 | +**复杂度分析** |
| 110 | +令 N 为数组长度, C 为 seen 的大小。C 的大小和数据范围有关,在这里 C 不会超过 32。 |
| 111 | + |
| 112 | +- 时间复杂度:$O(N*C)$ |
| 113 | +- 空间复杂度:$O(C)$ |
| 114 | + |
| 115 | +更多题解可以访问我的 LeetCode 题解仓库:https://github.com/azl397985856/leetcode 。 目前已经 37K star 啦。 |
| 116 | + |
| 117 | +关注公众号力扣加加,努力用清晰直白的语言还原解题思路,并且有大量图解,手把手教你识别套路,高效刷题。 |
| 118 | + |
| 119 | + |
0 commit comments