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

144 阅读2分钟

Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情

一、题目描述:

给你一个整数数组 nums 和一个整数 target 。

请你统计并返回 nums 中能满足其最小元素与最大元素的 和 小于或等于 target 的 非空 子序列的数目。

由于答案可能很大,请将结果对 109 + 7 取余后返回。

来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/nu… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

二、思路分析:

一看到子序列脑子中很快就想到了动态规划或者回溯,再仔细看下题目的意思,显然用回溯暴力应该可以做出来,但是应该是过不了的,因为题目说了结果很大,都要取余来获取最终的结果了,不过没事,反正很久没有写过回溯的题目了,正好回顾一下。回溯的题目无疑要注意去重,这里的相同元素是有不同的含义的,所以连去重都省了,那么回溯要做什么呢?把最大值/最小值改回原来的值。很快一份超时的代码就出炉来。

超时代码

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number}
 */
var numSubseq = function(nums, target) {
    let min=Infinity,max=-Infinity,cnt=0;
    let vis=new Array(nums.length).fill(false);
    nums.sort((a,b)=>a-b);
    dfs(0);
    return cnt%(10**9 + 7);
    function dfs(index){
        if(min+max>target){
            return;
        }
        if(min+max<=target){
            cnt++;
        }
        for(let i=index;i<nums.length;i++){
            vis[i]=true;
            let preMin=min;
            let preMax=max;
            if(min>nums[i]){
                min=nums[i];
            }
            if(max<nums[i]){
                max=nums[i];
            }
            dfs(i+1);
            vis[i]=false;
            min=preMin;
            max=preMax;
        }
    }
};

显然暴力很多时候是解决不了问题的,那我们就需要继续揣摩下题目了。仔细想想,其实我们只要找个最大值最小值,然后介于这两者之间的数能有多少种组合,那么答案就是几1了。那么可以分为下面几步解决这个问题

  • 从小到大对数组进行排序;
  • 查找 target-nums[i] 应该在的位置j;
  • 计算介于i和j之间可以组成的数的个数; 三、AC 代码:

代码一:

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number}
 */

var numSubseq = function(nums, target) {
  let count = BigInt(0), mod = BigInt(1e9 + 7);
  nums.sort((a, b) => a - b);
  let len = nums.length;
  for (let i = 0; i < len; i++) {
    let j = findMax(target - nums[i], nums);
    if (j >= i) {
      count = (count + quickPow(2, j - i) % mod) % mod;
    }
  }
  return count;
};

// 快速幂算法
function quickPow(x, n) {
  if (n === 0) return BigInt(1);
  let y = quickPow(x, Math.floor(n / 2));
  return n % 2 === 0 ? BigInt(y) * BigInt(y) : BigInt(y) * BigInt(y) * BigInt(x);
}

// 找到小于 value 的最大的索引
function findMax(value, arr) {
  let left = 0, right = arr.length - 1;
  while (left <= right) {
    let midIndex = left + ((right - left) >> 1);
    if (arr[midIndex] > value) {
      right = midIndex - 1;
    } else {
      if (midIndex === arr.length - 1 || arr[midIndex+1] > value) {
        return midIndex;
      } else {
        left = midIndex + 1;
      }
    }
  }
  return -1;
}

四、总结:

暴力是解决不了问题的哇