[JavaScript / leetcode] 2311. 小于等于 K 的最长二进制子序列

55 阅读1分钟

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

每日刷题 2022.10.20

题目

  • 给你一个二进制字符串 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 <= 10^9

解题思路

  • 倒叙遍历s, 酌情删除1,同时所有的0皆可保留。
  • 证明:所有的0皆可以保留:
    • 该命题等价于, 假设题解i删除了位置j上的0,那么复原位置j0同时删除最大位置上的1题解依然成立。
  • 容易得知,既然题解i成立,则位置j一定在最大位置1的位置右侧。同时,复原位置j上的0导致的净增大值,小于二倍的原最大位置1所代表的十进制数,即小于新最大位置上1所代表的十进制数。(参考等比数列求和)
  • 因此, 新十进制数 = 原十进制数 + 净增大值 - 新最大位置1代表的十进制数 < 原十进制数 <= k
  • 所以可知,所有被删除的0都可以通过相同方法复原,题解依然成立,即所有的0都不用删除

代码

/**
 * @param {string} s
 * @param {number} k
 * @return {number}
 */
var longestSubsequence = function(s, k) {
  let n = s.length, zero = 0;
  for(let i = 0; i < n; i++) {
    if(s[i] == 0) zero++;
  }
  let ans = 0;
  for(let i = n - 1; i >= 0; i--) {
    // if(ans > k) break; 
    if(s[i] == 1) {
      ans += Math.pow(2, n - 1 - i);
      if(ans > k) return zero;
      // console.log(Math.pow(2, n - 1 - i), k,ans)
      zero++;
    }
  }
  return zero;
};