题目来源: 1016. 子串能表示从 1 到 N 数字的二进制串
题目描述:
-
描述: 给定一个二进制字符串 s 和一个正整数 n,如果对于 [1, n] 范围内的每个整数,其二进制表示都是 s 的 子字符串 ,就返回 true,否则返回 false 。 子字符串 是字符串中连续的字符序列。
-
示例:
示例1:
输入:s = "0110", n = 3
输出:true
示例2:
输入:s = "0110", n = 4
输出:false
思路
思路1 题目给定一个二进制字符串s 和一个正整数n。我们需要判断1 到n 中的每一个整数是否都是s 的子字符串。
设[l,r] 表示大于等于l 且小于等于r 范围内的整数,对于n>1,一定存在,使得。那么对于内的数,它们都小于n,且二进制表示都为k 位,所以字符串s 中至少需要有个长度为k 的不同二进制数。记字符串s 的长度为m,则m 至少需要满足: 同理在[2k,n] 内的数,二进制表示都为k+1 位,所以m 同样需要满足:
若上述两个条件不满足,则可以直接返回False,否则,因为题目给定m≤1000,所以此时k 一定不大于11。又因为若s 的子串能包含全部二进制表示,则一定可以包含 全部二进制表示。因为我们可以将中数的二进制表示中的最高位的1 去掉并去掉对应的前导零,便可以得到 的全部二进制表示。
所以我们对字符串s 判断是否存在和的全部二进制表示即可。我们可以分别用长度为k 和k+1 的「滑动窗口」来枚举s 中全部长度为k 和k+1 的子串,将其加入哈希表,并判断哈希表中是否存在中的全部数即可
以上的分析都在n>1 的基础上,当n=1 时,我们只需要判断‘1’ 是否在s 中即可。
具体实现1
class Solution {
public boolean queryString(String s, int n) {
if (n == 1) {
return s.indexOf('1') != -1;
}
int k = 30;
while ((1 << k) >= n) {
--k;
}
if (s.length() < (1 << (k - 1)) + k - 1 || s.length() < n - (1 << k) + k + 1) {
return false;
}
return help(s, k, 1 << (k - 1), (1 << k) - 1) && help(s, k + 1, 1 << k, n);
}
public boolean help(String s, int k, int mi, int ma) {
Set<Integer> set = new HashSet<Integer>();
int t = 0;
for (int r = 0; r < s.length(); ++r) {
t = t * 2 + (s.charAt(r) - '0');
if (r >= k) {
t -= (s.charAt(r - k) - '0') << k;
}
if (r >= k - 1 && t >= mi && t <= ma) {
set.add(t);
}
}
return set.size() == ma - mi + 1;
}
}
复杂度分析1:
-
时间复杂度:O(logn+m),其中m 为字符串s 的长度。k 的求解时间开销为O(logn),「滑动窗口」枚举字符串s 每一个k 和k+1 子串的时间开销为O(m)。
-
空间复杂度:O(m),其中m 为字符串s 的长度,主要为哈希表的空间开销。