- 這一題是滿有趣的一題,我的靈感來自於 zkw 的線段樹(但沒有用到線段樹)。
- 觀察
1 ~ n
的樹,並將它排成 zkw 的線段樹,可以發現以下規則:- 滿足樹的子葉,則必定可以滿足其父節點,例如:找到
"1010"
,則可以滿足"101"
、"10"
、"1"
。 - 所以可以得到一個數學結論是:我們只需要找到
n ~ n/2+1
的數即可。
- 滿足樹的子葉,則必定可以滿足其父節點,例如:找到
[1] len = 1
[10] 11 len = 2
100 [101] 110 111 len = 3
1000 1001 [1010] 1011 1100 1101 1110 1111 len = 4
(8) num mask
^ 1<<3
- solution:
class Solution {
public:
bool queryString(string s, int n) {
if (n > 1979) return false; // 剪枝:當 n 太大時必為 false, 證明在下面
int len = 32 - __builtin_clz(n); // n 的位元長度
if (s.size() < len) return false; // s 長度連 window 都不夠時 return false
int mask = (1 << len) - 1; // 遮罩 用來控制 window 長度
unordered_set<int> seen; // 用來記錄數字是否出現過
int valid = n - n/2; // 總共需要收集到的數目
// init
int curr = 0;
for (int i = 0; i < len; i++) {
curr = ((curr << 1) | (s[i] & 1));
if (curr <= n && curr > n/2) seen.insert(curr);
if (seen.size() == valid) return true;
}
// rolling
for (int i = len; i < s.size(); i++) {
curr = ((curr << 1) | (s[i] & 1)) & mask;
if (curr <= n && curr > n/2) seen.insert(curr);
if (seen.size() == valid) return true;
}
return false;
}
};
- 剪枝的證明: 我來說明 1979 這個臨界值的計算過程:
當字串長度 = 1000 時,我們需要找到滿足以下條件的最大 n:
- 對於任意數字 n:
需要的位元數(bits) = ⌊log₂(n)⌋ + 1
需要表示的數字數量 = ⌊n/2⌋ + 1 (從 n/2+1 到 n)
- 在字串中可用的位置數:
available_pos = L - bits + 1
其中 L 是字串長度(1000)
- 要滿足:
需要表示的數字數量 ≤ available_pos
⌊n/2⌋ + 1 ≤ L - (⌊log₂(n)⌋ + 1) + 1
n/2 + 1 ≤ 1000 - log₂(n)
用程式來找這個值:
def check(n):
bits = n.bit_length() # 等同於 ⌊log₂(n)⌋ + 1
need_nums = n//2 + 1 # 需要表示的數字數量
available = 1000 - bits + 1
return need_nums <= available
# 二分搜尋找最大的 n
left, right = 1, 10000
while left + 1 < right:
mid = (left + right) // 2
if check(mid):
left = mid
else:
right = mid
# left 就是答案 1979
驗證 1979:
n = 1979
bits = 11 (因為 1979 < 2048 = 2¹¹)
need_nums = 990 (從 990 到 1979)
available = 1000 - 11 + 1 = 990
驗證 1980:
n = 1980
bits = 11
need_nums = 991 (從 991 到 1980)
available = 1000 - 11 + 1 = 990
此時 need_nums > available,所以 1980 不行
這表示:
- 1979 是最大可能的 n
- 因為剛好需要 990 個位置來表示所有需要的數字
- 如果 n = 1980,則需要 991 個位置,但我們只有 990 個可用位置
所以 1979 就是這個精確的臨界值。