|
| 1 | +--- |
| 2 | +title: 1545.找出第 N 个二进制字符串中的第 K 位:模拟 或 递归(数学) |
| 3 | +date: 2026-03-03 09:29:45 |
| 4 | +tags: [题解, LeetCode, 中等, 递归, 字符串, 模拟, 数学] |
| 5 | +categories: [题解, LeetCode] |
| 6 | +--- |
| 7 | + |
| 8 | +# 【LetMeFly】1545.找出第 N 个二进制字符串中的第 K 位:模拟 或 递归(数学) |
| 9 | + |
| 10 | +力扣题目链接:[https://leetcode.cn/problems/find-kth-bit-in-nth-binary-string/](https://leetcode.cn/problems/find-kth-bit-in-nth-binary-string/) |
| 11 | + |
| 12 | +<p>给你两个正整数 <code>n</code> 和 <code>k</code>,二进制字符串 <code>S<sub>n</sub></code> 的形成规则如下:</p> |
| 13 | + |
| 14 | +<ul> |
| 15 | + <li><code>S<sub>1</sub> = "0"</code></li> |
| 16 | + <li>当 <code>i > 1</code> 时,<code>S<sub>i</sub> = S<sub>i-1</sub> + "1" + reverse(invert(S<sub>i-1</sub>))</code></li> |
| 17 | +</ul> |
| 18 | + |
| 19 | +<p>其中 <code>+</code> 表示串联操作,<code>reverse(x)</code> 返回反转 <code>x</code> 后得到的字符串,而 <code>invert(x)</code> 则会翻转 x 中的每一位(0 变为 1,而 1 变为 0)。</p> |
| 20 | + |
| 21 | +<p>例如,符合上述描述的序列的前 4 个字符串依次是:</p> |
| 22 | + |
| 23 | +<ul> |
| 24 | + <li><code>S<sub>1 </sub>= "0"</code></li> |
| 25 | + <li><code>S<sub>2 </sub>= "0<strong>1</strong>1"</code></li> |
| 26 | + <li><code>S<sub>3 </sub>= "011<strong>1</strong>001"</code></li> |
| 27 | + <li><code>S<sub>4</sub> = "0111001<strong>1</strong>0110001"</code></li> |
| 28 | +</ul> |
| 29 | + |
| 30 | +<p>请你返回 <code>S<sub>n</sub></code> 的 <strong>第 <code>k</code> 位字符</strong> ,题目数据保证 <code>k</code> 一定在 <code>S<sub>n</sub></code> 长度范围以内。</p> |
| 31 | + |
| 32 | +<p> </p> |
| 33 | + |
| 34 | +<p><strong>示例 1:</strong></p> |
| 35 | + |
| 36 | +<pre> |
| 37 | +<strong>输入:</strong>n = 3, k = 1 |
| 38 | +<strong>输出:</strong>"0" |
| 39 | +<strong>解释:</strong>S<sub>3</sub> 为 "<strong>0</strong>111001",其第 1 位为 "0" 。 |
| 40 | +</pre> |
| 41 | + |
| 42 | +<p><strong>示例 2:</strong></p> |
| 43 | + |
| 44 | +<pre> |
| 45 | +<strong>输入:</strong>n = 4, k = 11 |
| 46 | +<strong>输出:</strong>"1" |
| 47 | +<strong>解释:</strong>S<sub>4</sub> 为 "0111001101<strong>1</strong>0001",其第 11 位为 "1" 。 |
| 48 | +</pre> |
| 49 | + |
| 50 | +<p><strong>示例 3:</strong></p> |
| 51 | + |
| 52 | +<pre> |
| 53 | +<strong>输入:</strong>n = 1, k = 1 |
| 54 | +<strong>输出:</strong>"0" |
| 55 | +</pre> |
| 56 | + |
| 57 | +<p><strong>示例 4:</strong></p> |
| 58 | + |
| 59 | +<pre> |
| 60 | +<strong>输入:</strong>n = 2, k = 3 |
| 61 | +<strong>输出:</strong>"1" |
| 62 | +</pre> |
| 63 | + |
| 64 | +<p> </p> |
| 65 | + |
| 66 | +<p><strong>提示:</strong></p> |
| 67 | + |
| 68 | +<ul> |
| 69 | + <li><code>1 <= n <= 20</code></li> |
| 70 | + <li><code>1 <= k <= 2<sup>n</sup> - 1</code></li> |
| 71 | +</ul> |
| 72 | + |
| 73 | + |
| 74 | +# 解题方法 |
| 75 | + |
| 76 | +## 解题方法一:模拟 |
| 77 | + |
| 78 | +写一个函数,求当前字符串的下一个字符串,一直模拟$n-1$次`next`就好了。注意最终返回下标`k-1`。 |
| 79 | + |
| 80 | ++ 时间复杂度$O(2^n)$ |
| 81 | ++ 空间复杂度$O(2^n)$ |
| 82 | + |
| 83 | +### AC代码 |
| 84 | + |
| 85 | +#### C++ |
| 86 | + |
| 87 | +```cpp |
| 88 | +/* |
| 89 | + * @LastEditTime: 2026-03-03 09:14:52 |
| 90 | + */ |
| 91 | +class Solution { |
| 92 | +private: |
| 93 | + void invert(string& s) { |
| 94 | + for (char& c : s) { |
| 95 | + c = c == '0' ? '1' : '0'; |
| 96 | + } |
| 97 | + } |
| 98 | + |
| 99 | + string next(string& now) { |
| 100 | + string ans = now + '1'; |
| 101 | + invert(now); |
| 102 | + reverse(now.begin(), now.end()); |
| 103 | + ans += now; |
| 104 | + return ans; |
| 105 | + } |
| 106 | +public: |
| 107 | + char findKthBit(int n, int k) { |
| 108 | + string now = "0"; |
| 109 | + for (int i = 2; i <= n; i++) { |
| 110 | + now = next(now); |
| 111 | + } |
| 112 | + return now[k - 1]; |
| 113 | + } |
| 114 | +}; |
| 115 | +``` |
| 116 | +
|
| 117 | +## 解题方法二:递归 + 数学 |
| 118 | +
|
| 119 | +对于字符串长度: |
| 120 | +
|
| 121 | +``` |
| 122 | +n1 = 1 |
| 123 | +n2 = 2 * n1 + 1 |
| 124 | +n3 = 2 * n2 + 1 |
| 125 | + |
| 126 | +... |
| 127 | + |
| 128 | +n_k = ? |
| 129 | +``` |
| 130 | +
|
| 131 | +答案是$n_k=2^k-1$,原因如下: |
| 132 | +
|
| 133 | +> $n_{k+1} = 2n_k + 1$ |
| 134 | +> |
| 135 | +> 两边都加一:$n_{k+1} + 1 = 2n_k + 1 + 1 = 2(n_k + 1)$ |
| 136 | +> |
| 137 | +> 令 $a_k = n_k + 1$,则:$a_{k+1} = 2 \cdot a_k$ |
| 138 | +> |
| 139 | +> $a_k$是一个公比为2的等比数列,且初项$a_1=n_1+1=2$,所以$a_k=2^k$ |
| 140 | +> |
| 141 | +> 由于$a_k = n_k + 1$,所以$n_k=a_k-1=2^k-1$。 |
| 142 | +
|
| 143 | +那么`findKthBit`就可以依据字符串的长度(计算自$n$)计算出要找的$k$在字符串中的哪个位置了: |
| 144 | +
|
| 145 | +> 字符串长度为$2^n-1$(记为$len$),说明生成这个字符串的上一个字符串的长度为$\frac{len}{2}$(记为$half\_len$)。 |
| 146 | +> |
| 147 | +> + 如果$k==half\_len+1$,则说明正好处在<code>S<sub>i</sub> = S<sub>i-1</sub> + "1" + reverse(invert(S<sub>i-1</sub>))</code>中间的`1`,直接返回`1`; |
| 148 | +> + 如果$k\leq half\_len$,则说明处在<code>S<sub>i-1</sub></code>部分,返回`findKthBit(n - 1, k)`; |
| 149 | +> + 否则,说明处在<code>reverse(invert(S<sub>i-1</sub>))</code>位置,$k$在$len$中是倒数第$len-k+1$个元素<span title="俺想出来的通俗易懂的解释哈哈哈">,</span>,返回`revert(findKthBit(n - 1, len - k + 1))`。 |
| 150 | +
|
| 151 | ++ 时间复杂度$O(n)$ |
| 152 | ++ 空间复杂度$O(n)$ |
| 153 | +
|
| 154 | +### AC代码 |
| 155 | +
|
| 156 | +#### C++ |
| 157 | +
|
| 158 | +```cpp |
| 159 | +/* |
| 160 | + * @LastEditTime: 2026-03-03 09:39:20 |
| 161 | + */ |
| 162 | +class Solution { |
| 163 | +private: |
| 164 | + inline char invert(char c) { |
| 165 | + return c == '0' ? '1' : '0'; |
| 166 | + } |
| 167 | +public: |
| 168 | + char findKthBit(int n, int k) { |
| 169 | + if (n == 1) { |
| 170 | + return '0'; |
| 171 | + } |
| 172 | + int len = (1 << n) - 1; |
| 173 | + int half_len = len >> 1; |
| 174 | + if (k == half_len + 1) { |
| 175 | + return '1'; |
| 176 | + } else if (k <= half_len) { |
| 177 | + return findKthBit(n - 1, k); |
| 178 | + } else { |
| 179 | + return invert(findKthBit(n - 1, len - k + 1)); // n = 2, k = 3 -> len = 3, half_len = 1, next_k = 1 |
| 180 | + } |
| 181 | + } |
| 182 | +}; |
| 183 | +``` |
| 184 | + |
| 185 | +#### Python |
| 186 | + |
| 187 | +```py |
| 188 | +''' |
| 189 | +LastEditTime: 2026-03-03 20:16:16 |
| 190 | +''' |
| 191 | +class Solution: |
| 192 | + def invert(self, n: str) -> str: |
| 193 | + return '1' if n == '0' else '0' |
| 194 | + |
| 195 | + def findKthBit(self, n: int, k: int) -> str: |
| 196 | + if n == 1: |
| 197 | + return '0' |
| 198 | + len = (1 << n) - 1 |
| 199 | + half = len >> 1 |
| 200 | + if k == half + 1: |
| 201 | + return '1' |
| 202 | + elif k <= half: |
| 203 | + return self.findKthBit(n - 1, k) |
| 204 | + else: |
| 205 | + return self.invert(self.findKthBit(n - 1, len - k + 1)) |
| 206 | +``` |
| 207 | + |
| 208 | +#### C++ |
| 209 | + |
| 210 | +当然也可以: |
| 211 | + |
| 212 | +```cpp |
| 213 | +/* |
| 214 | + * @LastEditTime: 2026-03-03 09:28:21 |
| 215 | + */ |
| 216 | +class Solution { |
| 217 | +public: |
| 218 | + char findKthBit(int n, int k, bool invert=false) { |
| 219 | + if (n == 1) { |
| 220 | + return invert ? '1' : '0'; |
| 221 | + } |
| 222 | + int len = (1 << n) - 1; |
| 223 | + int half_len = len >> 1; |
| 224 | + if (k == half_len + 1) { |
| 225 | + return invert ? '0' : '1'; |
| 226 | + } else if (k <= half_len) { |
| 227 | + return findKthBit(n - 1, k, invert); |
| 228 | + } else { |
| 229 | + return findKthBit(n - 1, len - k + 1, 1 ^ invert); // n = 2, k = 3 -> len = 3, half_len = 1, next_k = 1 |
| 230 | + } |
| 231 | + } |
| 232 | +}; |
| 233 | +``` |
| 234 | +
|
| 235 | +
|
| 236 | +> 同步发文于[CSDN](https://letmefly.blog.csdn.net/article/details/158621541)和我的[个人博客](https://blog.letmefly.xyz/),原创不易,转载经作者同意后请附上[原文链接](https://blog.letmefly.xyz/2026/03/03/LeetCode%201545.%E6%89%BE%E5%87%BA%E7%AC%ACN%E4%B8%AA%E4%BA%8C%E8%BF%9B%E5%88%B6%E5%AD%97%E7%AC%A6%E4%B8%B2%E4%B8%AD%E7%9A%84%E7%AC%ACK%E4%BD%8D/)哦~ |
| 237 | +> |
| 238 | +> 千篇源码题解[已开源](https://github.com/LetMeFly666/LeetCode) |
0 commit comments