1. 问题描述:
在第一行我们写上一个 0。接下来的每一行,将前一行中的0替换为01,1替换为10。给定行数 N 和序数 K,返回第 N 行中第 K个字符。(K从1开始)
例子:
输入: N = 1, K = 1
输出: 0
输入: N = 2, K = 1
输出: 0
输入: N = 2, K = 2
输出: 1
输入: N = 4, K = 5
输出: 1
解释:
第一行: 0
第二行: 01
第三行: 0110
第四行: 01101001
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/k-th-symbol-in-grammar
2. 思路分析:
① 一开始最容易想到的是模拟整个过程,从最开始的字符串"0"出发,通过循环生成第N行的字符串,然后返回字符串中K - 1的位置的字符对应的整数,但是由于N最大是30生成的字符串长度为2 ^ 29,而且我们是在循环中模拟依次生成第N行的数据,所以时间复杂度是非常高的
② 除了暴力枚举的方法之外,力扣提供了递归的解法,感觉思路挺不错而且比较容易理解,通过写出前面若干行的数据可以发现一个规律就是:第 K 个数字是上一行第 (K+1) / 2 个数字生成的。如果上一行的数字为 0,被生成的数字为 1 - (K%2),如果上一行的数字为 1,被生成的数字为 K%2,所以我们可以利用递归从第N行开始往上找,利用递归层层返回的特点就可以求解最终第N第K个的数字,并且使用递归寻找的时候数据规模在一步步减小,根据下面力扣提供的图就比较好理解递归的过程了
③ 除了这个规律之外,还可以发现一个很明显的规律是对于当前行前半部分与后半部分为翻转的关系,所以我们可以利用这个规律使用递归解决即可,如果当前找的第N行中的第K个数据位于第N行的后半部分那么第K个数字就位前半部分数字对应的镜像,我们只需要找到前部部分对应的数字然后对其异或即可得到位于后半部分的第K个数字,如果当前找的第N行中的第K个数据位于第N行的前半部分那么直接在前半部分找即可。其中第N行的长度为2 ^ (N - 1),镜像为2 ^ (N - 2),后半部分对应的镜像为K - 2 ^ (N - 2),画出具体的例子就比较清楚了
3. 代码如下:
暴力(超时):
class Solution:
def kthGrammar(self, N: int, K: int) -> int:
pre, res = "0", ""
for i in range(N):
for j in range(len(pre)):
if pre[j] == "0":
res += "01"
else:
res += "10"
pre = res
res = ""
return int(pre[K - 1])
递归1:
class Solution(object):
def kthGrammar(self, N, K):
if N == 1: return 0
return (1 - K%2) ^ self.kthGrammar(N-1, (K+1)/2)
递归2:
import math
class Solution:
def kthGrammar(self, N: int, K: int) -> int:
if N == 1: return 0
if K <= int(math.pow(2, N - 2)):
return self.kthGrammar(N - 1, K)
else:
return 1 ^ self.kthGrammar(N - 1, K - int(math.pow(2, N - 2)))