双指针题目:满足条件的子序列数目

文章目录

题目

标题和出处

标题:满足条件的子序列数目

出处:1498. 满足条件的子序列数目

难度

7 级

题目描述

要求

给定一个整数数组 nums \texttt{nums} nums 和一个整数 target \texttt{target} target。

返回 nums \texttt{nums} nums 中满足最小元素与最大元素的和小于或等于 target \texttt{target} target 的非空 子序列的数目。由于答案可能非常大,返回答案模 10 9 + 7 \texttt{10}^\texttt{9} + \texttt{7} 109+7 的余数。

示例

示例 1:

输入: nums = [3,5,6,7], target = 9 \texttt{nums = [3,5,6,7], target = 9} nums = [3,5,6,7], target = 9

输出: 4 \texttt{4} 4

解释:有 4 \texttt{4} 4 个子序列满足该条件。

3\] → 3 + 3 ≤ 9 \\texttt{\[3\]} \\rightarrow \\texttt{3} + \\texttt{3} \\le \\texttt{9} \[3\]→3+3≤9 \[3,5\] → 3 + 5 ≤ 9 \\texttt{\[3,5\]} \\rightarrow \\texttt{3} + \\texttt{5} \\le \\texttt{9} \[3,5\]→3+5≤9 \[3,5,6\] → 3 + 6 ≤ 9 \\texttt{\[3,5,6\]} \\rightarrow \\texttt{3} + \\texttt{6} \\le \\texttt{9} \[3,5,6\]→3+6≤9 \[3,6\] → 3 + 6 ≤ 9 \\texttt{\[3,6\]} \\rightarrow \\texttt{3} + \\texttt{6} \\le \\texttt{9} \[3,6\]→3+6≤9 **示例 2:** 输入: nums = \[3,3,6,8\], target = 10 \\texttt{nums = \[3,3,6,8\], target = 10} nums = \[3,3,6,8\], target = 10 输出: 6 \\texttt{6} 6 解释:有 6 \\texttt{6} 6 个子序列满足该条件: \[3\] \\texttt{\[3\]} \[3\]、 \[3\] \\texttt{\[3\]} \[3\]、 \[3,3\] \\texttt{\[3,3\]} \[3,3\]、 \[3,6\] \\texttt{\[3,6\]} \[3,6\]、 \[3,6\] \\texttt{\[3,6\]} \[3,6\]、 \[3,3,6\] \\texttt{\[3,3,6\]} \[3,3,6\]。 **示例 3:** 输入: nums = \[2,3,3,4,6,7\], target = 12 \\texttt{nums = \[2,3,3,4,6,7\], target = 12} nums = \[2,3,3,4,6,7\], target = 12 输出: 61 \\texttt{61} 61 解释:共有 63 \\texttt{63} 63 个非空子序列,其中 2 \\texttt{2} 2 个不满足条件( \[6,7\] \\texttt{\[6,7\]} \[6,7\]、 \[7\] \\texttt{\[7\]} \[7\])。有效子序列总数为 63 − 2 = 61 \\texttt{63} - \\texttt{2} = \\texttt{61} 63−2=61。 #### 数据范围 * 1 ≤ nums.length ≤ 10 5 \\texttt{1} \\le \\texttt{nums.length} \\le \\texttt{10}\^\\texttt{5} 1≤nums.length≤105 * 1 ≤ nums\[i\] ≤ 10 6 \\texttt{1} \\le \\texttt{nums\[i\]} \\le \\texttt{10}\^\\texttt{6} 1≤nums\[i\]≤106 * 1 ≤ target ≤ 10 6 \\texttt{1} \\le \\texttt{target} \\le \\texttt{10}\^\\texttt{6} 1≤target≤106 ## 前言 这道题要求计算数组 nums \\textit{nums} nums 中满足最小值与最大值之和不超过 target \\textit{target} target 的非空子序列的数目。当一个子序列的最小值与最大值确定时,在最小值与最大值之间的其余元素是否加入子序列不会影响子序列的最小值与最大值。如果最小值与最大值之间的其余元素有 x x x 个,则当前最小值与最大值确定的子序列个数是 2 x 2\^x 2x,其中 x x x 不超过数组 nums \\textit{nums} nums 的长度。为了快速计算任意 x x x 对应的 2 x 2\^x 2x,需要预计算从 0 0 0 到数组长度的每个整数对应的 2 2 2 的幂。在预计算 2 2 2 的幂之后,再计算满足条件的子序列数目。 ## 解法一 ### 思路和算法 由于这道题要求计算满足条件的子序列数目,子序列不要求在原数组中连续,因此可以将数组 nums \\textit{nums} nums 按升序排序,然后在排序后的数组中计算满足条件的子序列数目。 将数组 nums \\textit{nums} nums 排序之后,从左到右遍历数组 nums \\textit{nums} nums,对于下标 i i i,当 nums \[ i \] × 2 ≤ target \\textit{nums}\[i\] \\times 2 \\le \\textit{target} nums\[i\]×2≤target 时,一定存在下标 j ≥ i j \\ge i j≥i 使得 nums \[ i \] + nums \[ j \] ≤ target \\textit{nums}\[i\] + \\textit{nums}\[j\] \\le \\textit{target} nums\[i\]+nums\[j\]≤target,此时 nums \[ i \] \\textit{nums}\[i\] nums\[i\] 和 nums \[ j \] \\textit{nums}\[j\] nums\[j\] 可以分别作为满足条件的子序列中的最小值和最大值。 由于数组 nums \\textit{nums} nums 按升序排序,因此当 nums \[ i \] × 2 ≤ target \\textit{nums}\[i\] \\times 2 \\le \\textit{target} nums\[i\]×2≤target 时,对于下标 i i i 可以使用二分查找的做法找到满足 j ≥ i j \\ge i j≥i 且 nums \[ i \] + nums \[ j \] ≤ target \\textit{nums}\[i\] + \\textit{nums}\[j\] \\le \\textit{target} nums\[i\]+nums\[j\]≤target 的最大下标 j j j。二分查找的下界和上界分别记为 low \\textit{low} low 和 high \\textit{high} high,初始时 low \\textit{low} low 位于下标 i i i, high \\textit{high} high 位于数组的最大下标,每次查找取 mid \\textit{mid} mid 为 low \\textit{low} low 和 high \\textit{high} high 的平均数向上取整,执行如下操作。 * 如果 nums \[ mid \] ≤ target \\textit{nums}\[\\textit{mid}\] \\le \\textit{target} nums\[mid\]≤target,则最大下标大于等于 mid \\textit{mid} mid,因此在下标范围 \[ mid , high \] \[\\textit{mid}, \\textit{high}\] \[mid,high\] 中继续查找。 * 如果 nums \[ mid \] \> target \\textit{nums}\[\\textit{mid}\] \> \\textit{target} nums\[mid\]\>target,则最大下标小于 mid \\textit{mid} mid,因此在下标范围 \[ low , mid − 1 \] \[\\textit{low}, \\textit{mid} - 1\] \[low,mid−1\] 中继续查找。 当 low = high \\textit{low} = \\textit{high} low=high 时,查找结束,此时 low \\textit{low} low 为满足 j ≥ i j \\ge i j≥i 且 nums \[ i \] + nums \[ j \] ≤ target \\textit{nums}\[i\] + \\textit{nums}\[j\] \\le \\textit{target} nums\[i\]+nums\[j\]≤target 的最大下标 j j j。 当 nums \[ i \] \\textit{nums}\[i\] nums\[i\] 是子序列中的最小值时, nums \[ i \] \\textit{nums}\[i\] nums\[i\] 必须在子序列中出现,满足条件的子序列中的最大值可以是大于等于 nums \[ i \] \\textit{nums}\[i\] nums\[i\] 且小于等于 nums \[ j \] \\textit{nums}\[j\] nums\[j\] 的任意值,对于任意 i \< k ≤ j i \< k \\le j i\ target \\textit{nums}\[\\textit{left}\] + \\textit{nums}\[\\textit{right}\] \> \\textit{target} nums\[left\]+nums\[right\]\>target 时,将 right \\textit{right} right 向左移动,直到 nums \[ left \] + nums \[ right \] ≤ target \\textit{nums}\[\\textit{left}\] + \\textit{nums}\[\\textit{right}\] \\le \\textit{target} nums\[left\]+nums\[right\]≤target,此时 right \\textit{right} right 是当前 left \\textit{left} left 对应的使得 nums \[ left \] + nums \[ right \] ≤ target \\textit{nums}\[\\textit{left}\] + \\textit{nums}\[\\textit{right}\] \\le \\textit{target} nums\[left\]+nums\[right\]≤target 的最大下标。 2. 当 nums \[ left \] \\textit{nums}\[\\textit{left}\] nums\[left\] 是子序列中的最小值时, nums \[ left \] \\textit{nums}\[\\textit{left}\] nums\[left\] 必须在子序列中出现,满足条件的子序列中的最大值可以是大于等于 nums \[ left \] \\textit{nums}\[\\textit{left}\] nums\[left\] 且小于等于 nums \[ right \] \\textit{nums}\[\\textit{right}\] nums\[right\] 的任意值,对于任意 left \< k ≤ right \\textit{left} \< k \\le \\textit{right} left\ target) { right--; } int count = power2[right - left]; subsequences = (subsequences + count) % MODULO; left++; } return subsequences; } } ``` ### 复杂度分析 * 时间复杂度: O ( n log ⁡ n ) O(n \\log n) O(nlogn),其中 n n n 是数组 nums \\textit{nums} nums 的长度。需要 O ( n ) O(n) O(n) 的时间预计算 2 2 2 的幂,需要 O ( n log ⁡ n ) O(n \\log n) O(nlogn) 的时间将数组 nums \\textit{nums} nums 排序,排序后需要使用双指针遍历数组 nums \\textit{nums} nums,因此时间复杂度是 O ( n + n log ⁡ n + n ) = O ( n log ⁡ n ) O(n + n \\log n + n) = O(n \\log n) O(n+nlogn+n)=O(nlogn)。 * 空间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组 nums \\textit{nums} nums 的长度。需要创建长度为 n n n 的数组存储 2 2 2 的幂,排序需要 O ( log ⁡ n ) O(\\log n) O(logn) 的递归调用栈空间,因此空间复杂度是 O ( n + log ⁡ n ) = O ( n ) O(n + \\log n) = O(n) O(n+logn)=O(n)。

相关推荐
汉克老师1 天前
GESP2025年3月认证C++五级( 第三部分编程题(1、平均分配))
c++·算法·贪心算法·排序·gesp5级·gesp五级
漂流瓶jz2 天前
UVA-1152 和为0的4个值 题解答案代码 算法竞赛入门经典第二版
数据结构·算法·二分查找·题解·aoapc·算法竞赛入门经典·uva
Irene19916 天前
数据排序为什么默认升序
算法·排序
浅念-7 天前
分治算法专题|LeetCode高频经典题目详细题解
数据结构·c++·算法·leetcode·职场和发展·排序·分治
qeen8711 天前
【算法笔记】双指针及其经典例题解析
c++·笔记·算法·双指针
漂流瓶jz16 天前
UVA-120 煎饼 题解答案代码 算法竞赛入门经典第二版
数据结构·c++·算法·排序·aoapc·算法竞赛入门经典·uva
闻缺陷则喜何志丹16 天前
【排序 离散化 二维前缀和】 P7149 [USACO20DEC] Rectangular Pasture S|普及+
c++·算法·排序·离散化·二维前缀和
闻缺陷则喜何志丹18 天前
【排序】P6149 [USACO20FEB] Triangles S|普及+
c++·算法·排序·洛谷
小辉同志19 天前
74. 搜索二维矩阵
c++·leetcode·矩阵·二分查找
拳里剑气20 天前
C++算法:二分查找
c++·算法·二分查找·学习方法