给定一个二进制字符串 s 和一个正整数 n,如果对于 [1, n] 范围内的每个整数, _其二进制表示都是 s 的 子字符串 ,就返回 true,否则返回 false _。
子字符串 是字符串中连续的字符序列。
示例 1:
**输入:** s = "0110", n = 3
**输出:** true
示例 2:
**输入:** s = "0110", n = 4
**输出:** false
提示:
1 <= s.length <= 1000
s[i] 不是 '0' 就是 '1'
1 <= n <= 109
方法一:数学 + 滑动窗口 + 哈希表
思路与算法
题目给定一个二进制字符串 s 和一个正整数 n。我们需要判断 1 到 n 中的每一个整数是否都是 s 的子字符串。
设 [l, r] 表示大于等于 l 且小于等于 r 范围内的整数,对于 n > 1,一定存在 k \in \mathbb{N^+,使得 2^k \le n < 2^{k + 1。那么对于 [2^{k-1}, 2^{k}-1] 内的数,它们都小于 n,且二进制表示都为 k 位,所以字符串 s 中至少需要有 2^{k-1 个长度为 k 的不同二进制数。记字符串 s 的长度为 m,则 m 至少需要满足:
m \ge 2 ^ {k - 1} + k - 1
同理在 [2^k, n] 内的数,二进制表示都为 k + 1 位,所以 m 同样需要满足:
m \ge n - 2^k + k + 1
若上述两个条件不满足,则可以直接返回 False,否则,因为题目给定 m \le 1000,所以此时 k 一定不大于 11。又因为若 s 的子串能包含 [2^{k-1}, 2^k-1] 全部二进制表示,则一定可以包含 [1, 2^k-1] 全部二进制表示。因为我们可以将 [2^{k-1}, 2^k-1] 中数的二进制表示中的最高位的 1 去掉并去掉对应的前导零,便可以得到 [0, 2^{k-1} - 1] 的全部二进制表示。
所以我们对字符串 s 判断是否存在 [2^{k-1}, 2^k-1] 和 [2^k, n] 的全部二进制表示即可。我们可以分别用长度为 k 和 k + 1 的「滑动窗口」来枚举 s 中全部长度为 k 和 k + 1 的子串,将其加入哈希表,并判断哈希表中是否存在 [2^{k-1}, n] 中的全部数即可。
以上的分析都在 n > 1 的基础上,当 n = 1 时,我们只需要判断 `1’ 是否在 s 中即可。
classSolution { public: boolhelp(const string& s, int k, int mi, int ma){ unordered_set<int> st; int t = 0; for (int r = 0; r < s.size(); ++r) { t = t * 2 + (s[r] - '0'); if (r >= k) { t -= (s[r - k] - '0') << k; } if (r >= k - 1 && t >= mi && t <= ma) { st.insert(t); } } return st.size() == ma - mi + 1; }
boolqueryString(string s, int n){ if (n == 1) { return s.find('1') != -1; } int k = 30; while ((1 << k) > n) { --k; } if (s.size() < (1 << (k - 1)) + k - 1 || s.size() < n - (1 << k) + k + 1) { returnfalse; } returnhelp(s, k, 1 << (k - 1), (1 << k) - 1) && help(s, k + 1, 1 << k, n); } };
classSolution { publicbooleanqueryString(String s, int n) { if (n == 1) { return s.indexOf('1') != -1; } intk=30; while ((1 << k) > n) { --k; } if (s.length() < (1 << (k - 1)) + k - 1 || s.length() < n - (1 << k) + k + 1) { returnfalse; } return help(s, k, 1 << (k - 1), (1 << k) - 1) && help(s, k + 1, 1 << k, n); }
publicbooleanhelp(String s, int k, int mi, int ma) { Set<Integer> set = newHashSet<Integer>(); intt=0; for (intr=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; } }
publicclassSolution { publicboolQueryString(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) { returnfalse; } return Help(s, k, 1 << (k - 1), (1 << k) - 1) && Help(s, k + 1, 1 << k, n); }
publicboolHelp(string s, int k, int mi, int ma) { ISet<int> set = new HashSet<int>(); int t = 0; for (int r = 0; r < s.Length; ++r) { t = t * 2 + (s[r] - '0'); if (r >= k) { t -= (s[r - k] - '0') << k; } if (r >= k - 1 && t >= mi && t <= ma) { set.Add(t); } } returnset.Count == ma - mi + 1; } }
classSolution: defqueryString(self, s: str, n: int) -> bool: defhelp(s, k, mi, ma): st = set() t = 0 for r inrange(len(s)): t = t * 2 + (int)(s[r]) if r >= k: t -= int(s[r - k]) << k if r >= k - 1and t >= mi and t <= ma: st.add(t) returnlen(st) == ma - mi + 1 if n == 1: return s.find('1') != -1 k = 30 while (1 << k) >= n: k -= 1 iflen(s) < (1 << (k - 1)) + k - 1orlen(s) < n - (1 << k) + k + 1: returnFalse returnhelp(s, k, 1 << (k - 1), (1 << k) - 1) andhelp(s, k + 1, 1 << k, n)
funchelp(s string, k int, mi int, ma int)bool { st := map[int]struct{}{} t := 0 for r := 0; r < len(s); r++ { t = (t << 1) + int(s[r] - '0') if r >= k { t -= int(s[r - k] - '0') << k } if r >= k - 1 && t >= mi && t <= ma { st[t] = struct{}{} } } returnlen(st) == ma - mi + 1 }
funcqueryString(s string, n int)bool { if n == 1 { return strings.Contains(s, "1") } k := 30 for (1 << k) >= n { k-- } iflen(s) < (1 << (k - 1)) + k - 1 || len(s) < n - (1 << k) + k + 1 { returnfalse } return help(s, k, 1 << (k - 1), (1 << k) - 1) && help(s, k + 1, 1 << k, n) }
boolhelp(constchar* s, int k, int mi, int ma) { HashItem *st = NULL; int t = 0; for (int r = 0; s[r] != '\0'; ++r) { t = t * 2 + (s[r] - '0'); if (r >= k) { t -= (s[r - k] - '0') << k; } if (r >= k - 1 && t >= mi && t <= ma) { hashAddItem(&st, t); } } int count = HASH_COUNT(st); hashFree(&st); return count == ma - mi + 1; }
boolqueryString(char * s, int n) { int len = strlen(s); if (n == 1) { returnstrchr(s, '1') != NULL; } int k = 30; while ((1 << k) > n) { --k; } if (len < (1 << (k - 1)) + k - 1 || len < n - (1 << k) + k + 1) { returnfalse; } return help(s, k, 1 << (k - 1), (1 << k) - 1) && help(s, k + 1, 1 << k, n); }
var queryString = function(s, n) { if (n === 1) { return [...s].indexOf('1') !== -1; } let k = 30; while ((1 << k) > n) { --k; } if (s.length < (1 << (k - 1)) + k - 1 || s.length < n - (1 << k) + k + 1) { returnfalse; } returnhelp(s, k, 1 << (k - 1), (1 << k) - 1) && help(s, k + 1, 1 << k, n); }
consthelp = (s, k, mi, ma) => { const set = newSet(); let t = 0; for (let r = 0; r < s.length; ++r) { t = t * 2 + (s[r].charCodeAt() - '0'.charCodeAt()); if (r >= k) { t -= (s[r - k].charCodeAt() - '0'.charCodeAt()) << k; } if (r >= k - 1 && t >= mi && t <= ma) { set.add(t); } } return set.size === ma - mi + 1; };
复杂度分析
时间复杂度:O(\log n + m),其中 m 为字符串 s 的长度。k 的求解时间开销为 O(\log n),「滑动窗口」枚举字符串 s 每一个 k 和 k + 1 子串的时间开销为 O(m)。