题目
给你一个二进制字符串 s 和一个正整数 k 。
请你返回 s 的 最长 子序列,且该子序列对应的 二进制 数字小于等于 k 。
注意:
子序列可以有 前导 0 。
空字符串视为 0 。
子序列 是指从一个字符串中删除零个或者多个字符后,不改变顺序得到的剩余字符序列。示例 1:
输入:s = “1001010”, k = 5
输出:5
解释:s 中小于等于 5 的最长子序列是 “00010” ,对应的十进制数字是 2 。
注意 “00100” 和 “00101” 也是可行的最长子序列,十进制分别对应 4 和 5 。
最长子序列的长度为 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来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/longest-binary-subsequence-less-than-or-equal-to-k
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
返回的s子序列(记为t)的二进制表示需要满足不大于k,因此除去t的前导0后,要么t的长度小于k的二进制长度(记为m),要么长度相等且十进制表示不大于k。
对于s长度和m之间的关系,可以分为两种情况:
1、当s长度不小于m,我们从低位使用一个长度为m的滑窗,向左滑动,依次比较滑窗内二进制串和k的二进制串的大小关系,如果前者不大于后者,那么找到了一种解,再加上前面的0充当前导0即可。
有可能遍历完s之后,都找不到一个不小于k的子序列,这种情况下,一定可以取长度为m-1的序列,满足该序列的十进制不大于k。
为什么找到的第一个解一定是最长的呢,因为我们是倒着遍历,前面的前导0一定是最多的。
2、当s长度小于m,s串本身的十进制一定小于k,返回s长度即可。
代码
class Solution {
public int longestSubsequence(String s, int k) {
int n = s.length();
String kstr = Integer.toBinaryString(k);
int m = kstr.length();
// s长度小于k二进制长度,s十进制一定小于k,返回s.length()
if (n < m) {
return n;
}
int ans = 0;
int i = 0;
// 从s的低位开始取长度为m的滑窗,和k进行比较
for (i = n - m; i >= 0; i--) {
// 如果s中长度为m的子序列不大于k,就表示找到了一个解,长度至少为m
if (compare(s.substring(i, i + m), kstr)) {
ans = m;
break;
}
}
// 没有找到合法的解,但是长度m-1的序列一定小于k
if (ans == 0) {
return m - 1;
}
// 加上前面的0充当前导0
for (int j = i - 1; j >= 0; j--) {
if (s.charAt(j) == '0') {
ans++;
}
}
return ans;
}
// 从高位依次比较s和t,如果s不大于t,那么返回true,反之返回false
private boolean compare(String s, String t) {
int m = s.length();
for (int i = 0; i < m; i++) {
if (s.charAt(i) > t.charAt(i)) {
return false;
} else if (s.charAt(i) < t.charAt(i)){
return true;
}
}
return true;
}
}