[ 贪心 ] 2311. 小于等于 K 的最长二进制子序列

208 阅读1分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第23天,点击查看活动详情

每日刷题 2022.06.21

题目

  • 给你一个二进制字符串 s 和一个正整数 k 。
  • 请你返回 s 的 最长 子序列,且该子序列对应的 二进制 数字小于等于 k 。
  • 注意:
    • 子序列可以有 前导 0 。
    • 空字符串视为 0 。
    • 子序列 是指从一个字符串中删除零个或者多个字符后,不改变顺序得到的剩余字符序列。

示例

  • 示例1
输入:s = "1001010", k = 5
输出:5
解释:s 中小于等于 5 的最长子序列是 "00010" ,对应的十进制数字是 2 。
注意 "00100""00101" 也是可行的最长子序列,十进制分别对应 45 。
最长子序列的长度为 5 ,所以返回 5
  • 示例2
输入:s = "00101001", k = 1
输出:6
解释:"000001" 是 s 中小于等于 1 的最长子序列,对应的十进制数字是 1 。
最长子序列的长度为 6 ,所以返回 6 。

提示

  • 1 <= s.length <= 1000
  • s[i] 要么是 '0' ,要么是 '1' 。
  • 1 <= k <= 109

解题思路

  • 周赛的第三题,使用贪心的做法
  • 分析题目:需要查找最长子序列(注:子序列是可以不连续的,通过删除原串的0~多个后得到的序列;子串是必须连续的)。
  • 并且题目中还提到:子序列可以有前导0
  • 综合分析:既然可以存在前导0,那么为了能够凑成最长的子序列,那么就将所有的0全部留下来,因为0是不会影响结果的,但是可以增长序列的长度。
  • 那么接下来就需要考虑哪些1可以留下来,哪些1需要删除
    • 高位的1比低位的1代表着更大的数值,也就是说多个低位的1才能拼凑成一个高位的1,因此高位对于最长子序列的贡献,就比多个低位的1对最长子序列的贡献小。
    • 总结:从低位到高位进行遍历,查看依次插入低位1之后,是否小于等于K,找到第一个大于K的元素,那么其前一个就是最长的子序列,直接返回结果即可。

AC代码

/**
 * @param {string} s
 * @param {number} k
 * @return {number}
 */
var longestSubsequence = function(s, k) {
  let n = s.length, zero = 0;
  // 统计原串中的0的个数,所有的0都可以用上,因为要找的是最长子序列,可以存在前导0
  for(let i = 0; i < n; i++) {
    if(s[i] == 0) zero++;
  }
  // 从低位到高位判断1,插入后是否小于等于k
  let ans = 0;
  for(let i = n - 1; i >= 0; i--) {
    // 位置为1的时候,判断插入后的数值与K的大小
    if(s[i] == 1) {
      ans += Math.pow(2, n - 1 - i);
      // 找到第一个大于等于K的数时,返回最长子序列长度
      if(ans > k) return zero;
      // console.log(Math.pow(2, n - 1 - i), k,ans)
      zero++;
    }
  }
  return zero;
};