--- title: 通过位运算快速生成所有的子序列 createTime: 2026/01/09 16:15:00 cover: /images/elysia/10.jpg coverStyle: layout: right permalink: /archives/ea20bdda-0d49-4472-a647-2e305a930d11/ --- ## 一、子序列的本质 子序列是从原字符串中选择任意数量的字符(可以是 0 个、1 个、...、所有字符),且保持这些字符的原始顺序形成的字符串。 例如:原字符串 `"abc"` 的子序列包括 `"a"`, `"b"`, `"c"`, `"ab"`, `"ac"`, `"bc"`, `"abc"` 和空字符串 `""`(共 $2^3 = 8$ 种可能性)。 ## 二、位掩码(Bitmask)的引入 假设字符串长度为 `n`,我们可以用一个 `n` 位的二进制数来表示是否选择某个字符: - 二进制数的每一位对应原字符串的一个字符。 - 如果某一位是 `1`,表示选择对应的字符;如果是 `0`,表示不选择。 例如,对于字符串 `"abc"`(`n=3`): | 二进制数 | 十进制 | 选择情况 | 生成子序列 | | :--- | :--- | :--- | :--- | | `101` | 5 | 选择第 1 个字符 "a" 和第 3 个字符 "c" | `"ac"` | | `010` | 2 | 选择第 2 个字符 "b" | `"b"` | ## 三、遍历所有可能的二进制数 所有可能的二进制数范围是 `0`(全 0)到 $2^n - 1$(全 1)。例如,`n=3` 时: - 范围是 `000` (0) 到 `111` (7),共 $2^3 = 8$ 种可能。 - 每个二进制数对应一种子序列选择方式。 - 如果包含 `000` (0),对应空字符串 `""`。若不需要空字符串,可以从 `1` 开始遍历。 ## 四、如何将二进制数转换为子序列? ### 1. 外层循环:遍历所有可能的二进制数 对于 `n=3`,遍历 `1` (`001`) 到 `7` (`111`)。 ### 2. 内层循环:检查每一位是否为 1 - 对于每个二进制数 `mask`,遍历其每一位 `i`(从 `0` 到 `n-1`)。 - 如果 `mask` 的第 `i` 位是 `1`(即 `mask & (1 << i)` 为真),则将原字符串的第 `i` 个字符加入子序列。 ## 五、代码示例 ```python def get_all_subsequences(s): n = len(s) subsequences = [] # 遍历 1 到 2^n - 1 (排除空字符串) for mask in range(1, 1 << n): subseq = [] for i in range(n): # 检查 mask 的第 i 位是否为 1 if mask & (1 << i): subseq.append(s[i]) subsequences.append(''.join(subseq)) return subsequences # 测试 print(get_all_subsequences("abc")) # 输出: ['a', 'b', 'ab', 'c', 'ac', 'bc', 'abc'] ``` ## 六、应用场景 在处理类似 **X 质数** 的问题时,我们需要检查一个数字的所有数字子序列。通过这种位运算的方式,可以非常高效地生成所有组合并进行判断。 ```python def solve_x_prime(num): s = str(num) n = len(s) for mask in range(1, 1 << n): subseq_str = "" for i in range(n): if mask & (1 << i): subseq_str += s[i] # 将生成的子序列转换为整数并判断是否为质数 p = int(subseq_str) if is_prime(p): return True return False ```