LC 779. 第K个语法符号

题目描述

这是 LeetCode 上的 779. 第K个语法符号 ,难度为 中等

我们构建了一个包含 n 行( 索引从 1 开始 )的表。首先在第一行我们写上一个 0。接下来的每一行,将前一行中的 0 替换为 011 替换为 10

  • 例如,对于 n = 3,第 1 行是 0 ,第 2 行是 01,第 3 行是 0110

给定行数 n 和序数 k,返回第 n 行中第 k 个字符。( k 从索引 1 开始)

示例 1:

1
2
3
4
5
输入: n = 1, k = 1

输出: 0

解释: 第一行:0

示例 2:
1
2
3
4
5
6
7
输入: n = 2, k = 1

输出: 0

解释:
第一行: 0
第二行: 01

示例 3:
1
2
3
4
5
6
7
输入: n = 2, k = 2

输出: 1

解释:
第一行: 0
第二行: 01

提示:

  • $1 <= n <= 30$
  • $1 <= k <= 2^{n - 1}$

递归(倒推验证)

整理一下条件:首行为 0,每次用当前行拓展出下一行时,字符数量翻倍(将 0 拓展为 01,将 1 拓展为 10),且字符种类仍为 01

要求我们输出第 $n$ 行第 $k$ 列的字符,我们可以通过「倒推验证」的方式来求解:假设第 $n$ 行第 $k$ 为 1,若能倒推出首行为 $0$,说明假设成立,返回 1,否则返回 0

倒推验证可通过实现递归函数 int dfs(int r, int c, int cur) 来做,含义为当第 $r$ 行第 $c$ 列的字符为 $cur$ 时,首行首列字符为何值。同时实现该函数是容易的:

  • 若「当前列 $c$ 为偶数且 $cur = 0$」或「当前列 $c$ 为奇数且 $cur = 1$」时,说明当前列所在的组为 10,由此可推出其是由上一行的 1 拓展而来,结合每次拓展新行字符数量翻倍的条件,可知是由第 $n - 1$ 行的第 $\left \lfloor \frac{c - 1}{2} \right \rfloor + 1$ 列的 1 拓展而来,递归处理;
  • 否则,同理,必然是上一行(第 $n - 1$ 行)对应位置的 0 拓展而来,递归处理。

最终,当倒推到首行时,我们找到了递归出口,直接返回 cur

Java 代码:

1
2
3
4
5
6
7
8
9
10
class Solution {
public int kthGrammar(int n, int k) {
return dfs(n, k, 1) == 0 ? 1 : 0;
}
int dfs(int r, int c, int cur) {
if (r == 1) return cur;
if ((c % 2 == 0 && cur == 0) || (c % 2 == 1 && cur == 1)) return dfs(r - 1, (c - 1) / 2 + 1, 1);
else return dfs(r - 1, (c - 1) / 2 + 1, 0);
}
}

TypeScript 代码:
1
2
3
4
5
6
7
8
function kthGrammar(n: number, k: number): number {
function dfs(r: number, c: number, cur: number): number {
if (r == 1) return cur
if ((c % 2 == 0 && cur == 0) || (c % 2 == 1 && cur == 1)) return dfs(r - 1, Math.floor((c - 1) / 2) + 1, 1)
else return dfs(r - 1, Math.floor((c - 1) / 2) + 1, 0)
}
return dfs(n, k, 1) == 0 ? 1 : 0
}

Python 代码:
1
2
3
4
5
6
7
8
9
10
class Solution:
def kthGrammar(self, n: int, k: int) -> int:
def dfs(r, c, cur):
if r == 1:
return cur
if (c % 2 == 0 and cur == 0) or (c % 2 == 1 and cur == 1):
return dfs(r - 1, (c - 1) // 2 + 1, 1)
else:
return dfs(r - 1, (c - 1) // 2 + 1, 0)
return 1 if dfs(n, k, 1) == 0 else 0

  • 时间复杂度:$O(n)$
  • 空间复杂度:忽略递归带来的额外空间开销,复杂度为 $O(1)$

最后

这是我们「刷穿 LeetCode」系列文章的第 No.779 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。

在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。

为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode

在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!