LeetCode 779. 第K个语法符号
题目描述


题意分析
第一行只有
0,之后每个0生成01,每个1生成10。行号和位置都从1开始,要求第n行第k个符号。每生成一行长度都会翻倍,第
n行有 $2^{n-1}$ 个字符。只查询一个位置时,无需构造整行,只需确定它在生成过程中的祖先路径,以及这条路径如何改变最初的0。
解法:位计数
核心思路
[!blue]
把生成过程看成一棵二叉树:一个符号生成的第一个字符是左孩子,第二个字符是右孩子。由
0 -> 01和1 -> 10可知,左孩子始终等于父值,右孩子始终是父值的翻转。因此叶子最终为多少,只取决于从根到它走了多少次右分支。先把位置变成零基下标
q = k - 1。父节点下标为p时,它的两个孩子下标分别是2 * p和2 * p + 1,所以孩子下标的最低位为0表示左分支,为1表示右分支;去掉最低位,就回到了父节点下标。不断向上重复,恰好读取完q的二进制位,每个1对应路径上的一次右分支。根节点是
0,翻转两次会恢复原值,所以右分支次数为偶数时结果为0,奇数时为1。直接统计k - 1的置位数并对2取模,就得到答案,不必真的执行逐层递归。从根向下看,这条路径使用
n - 1位,不足的高位补零。增加合法行数只会在同一位置的路径前增加左分支,不改变翻转次数,所以公式不需要使用n;n的作用是保证给出的k在这一行中存在。
解题步骤
- 位置先减一。
- 统计二进制一的数量。
- 返回奇偶。
k == 1时,零基下标为零,整条路径都是左分支,结果始终为零;这也覆盖第一行。题目限制n <= 30,k - 1能放进当前整数类型,Java 的bitCount与 Go 的OnesCount都能完整统计所需位数。
代码实现
class Solution {
public int kthGrammar(int n, int k) {
// 零基位置中的一,表示从根到该位置的右分支次数
int ones = Integer.bitCount(k - 1);
return ones % 2;
}
}
import "math/bits"
func kthGrammar(n int, k int) int {
// 零基位置中的一,表示从根到该位置的右分支次数
ones := bits.OnesCount(uint(k - 1))
return int(ones % 2)
}
复杂度分析
- 时间复杂度:固定整数位宽下 $O(1)$。
- 空间复杂度:$O(1)$。
关键点总结
[!green]
- n 保证位置合法,公式无需构造对应层。
- 路径编码采用零基位置。
易错点总结
[!yellow]
- 直接统计 k,会把第一行首个符号算成一。
- 把层数也加进奇偶,会错误改变同一位置在更深行的结果。
- 只使用较窄位宽,会丢掉高位翻转信息。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 191. 位1的个数 | 简单 | 第k项可由k-1二进制置位数的奇偶性确定,位计数替代指数长度的展开。 |
| 1545. 找出第 N 个二进制字符串中的第 K 位 | 中等 | 同样查询递归生成串的单个位置,原题还包含反转与取反,需要按递归结构映射位置。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!