返回介绍

solution / 0700-0799 / 0779.K-th Symbol in Grammar / README

发布于 2024-06-17 01:03:34 字数 4440 浏览 0 评论 0 收藏 0

779. 第 K 个语法符号

English Version

题目描述

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

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

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


示例 1:

输入: n = 1, k = 1
输出: 0
解释: 第一行:0

示例 2:

输入: n = 2, k = 1
输出: 0
解释: 
第一行: 0 
第二行: 01

示例 3:

输入: n = 2, k = 2
输出: 1
解释:
第一行: 0
第二行: 01

 

提示:

  • 1 <= n <= 30
  • 1 <= k <= 2n - 1

解法

方法一:递归

我们先来看一下前几行的规律:

n = 1: 0
n = 2: 0 1
n = 3: 0 1 1 0
n = 4: 0 1 1 0 1 0 0 1
n = 5: 0 1 1 0 1 0 0 1 1 0 0 1 0 1 1 0
...

可以发现,每一行的前半部分和上一行完全一致,后半部分是上一行的反转。注意,这里的“反转”指的是 $0$ 变 $1$, $1$ 变 $0$。

如果 $k$ 在前半部分,那么第 $k$ 个字符就是上一行的第 $k$ 个字符,直接递归 $kthGrammar(n - 1, k)$ 即可。

如果 $k$ 在后半部分,那么第 $k$ 个字符就是上一行的第 $k - 2^{n - 2}$ 个字符的反转,即 $kthGrammar(n - 1, k - 2^{n - 2}) \oplus 1 $。

时间复杂度 $O(n)$,空间复杂度 $O(n)$。

class Solution:
  def kthGrammar(self, n: int, k: int) -> int:
    if n == 1:
      return 0
    if k <= (1 << (n - 2)):
      return self.kthGrammar(n - 1, k)
    return self.kthGrammar(n - 1, k - (1 << (n - 2))) ^ 1
class Solution {
  public int kthGrammar(int n, int k) {
    if (n == 1) {
      return 0;
    }
    if (k <= (1 << (n - 2))) {
      return kthGrammar(n - 1, k);
    }
    return kthGrammar(n - 1, k - (1 << (n - 2))) ^ 1;
  }
}
class Solution {
public:
  int kthGrammar(int n, int k) {
    if (n == 1) return 0;
    if (k <= (1 << (n - 2))) return kthGrammar(n - 1, k);
    return kthGrammar(n - 1, k - (1 << (n - 2))) ^ 1;
  }
};
func kthGrammar(n int, k int) int {
  if n == 1 {
    return 0
  }
  if k <= (1 << (n - 2)) {
    return kthGrammar(n-1, k)
  }
  return kthGrammar(n-1, k-(1<<(n-2))) ^ 1
}

方法二:位运算 + 脑筋急转弯

题目中索引从 $1$ 开始,我们将 $k$ 改成 $k-1$,将索引转换为从 $0$ 开始。在接下来的讨论中,索引均从 $0$ 开始。

仔细观察,一行中的第 $i$ 个字符,会在第 $2i$ 和第 $2i+1$ 个位置产生两个字符。

0 1 1 0 1 0 0 1 1 0 0 1 0 1 1 0

如果第 $i$ 个字符是 $0$,那么在位置 $2i$ 和 $2i+1$ 产生的字符分别是 $0$ 和 $1$;如果第 $i$ 个字符是 $1$,产生的字符是 $1$ 和 $0$。

0 1 1 0 1 0 0 1 1 0 0 1 0 1 1 0
    ^   * *
0 1 1 0 1 0 0 1 1 0 0 1 0 1 1 0
    ^     * *

可以发现,第 $2i$ (偶数位)个字符总是和第 $i$ 个字符相同,而第 $2i+1$ (奇数位)个字符是第 $i$ 个字符的反转。也就是说,奇数位上的字符总是发生了一次反转而来的。反转偶数次,字符不变;反转奇数次,相当于反转了一次。

因此,我们只需要看 $k$ 这个数字是否是奇数,若是,累计一次反转。然后将 $k$ 除以 $2$,继续判断,并累计反转次数,直至 $k$ 为 $0$。

最后判断反转的次数是否为奇数,是则答案为 $1$,否则为 $0$。

以上累计反转次数的过程,实际上等价于求 $k$ 的二进制表示中,有多少位是 $1$。

时间复杂度 $O(\log k)$,空间复杂度 $O(1)$。

class Solution:
  def kthGrammar(self, n: int, k: int) -> int:
    return (k - 1).bit_count() & 1
class Solution {
  public int kthGrammar(int n, int k) {
    return Integer.bitCount(k - 1) & 1;
  }
}
class Solution {
public:
  int kthGrammar(int n, int k) {
    return __builtin_popcount(k - 1) & 1;
  }
};
func kthGrammar(n int, k int) int {
  return bits.OnesCount(uint(k-1)) & 1
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文