大家好,我是挨打的阿木木,爱好算法的前端摸鱼老。最近会频繁给大家分享我刷算法题过程中的思路和心得。如果你也是想提高逼格的摸鱼老,欢迎关注我,一起学习。
题目
779. 第K个语法符号
在第一行我们写上一个 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
复制代码
注意:
N
的范围[1, 30]
.K
的范围[1, 2^(N-1)]
.
思路
- 写一个变化的方法,每一轮把0替换成01,1替换为10;
- 经过一轮循环,做替换后存储最终的字符串为
result
; result
的第[k-1]
项就是我们要找的值了。
实现
/**
* @param {number} n
* @param {number} k
* @return {number}
*/
var kthGrammar = function(n, k) {
let result = "0";
while (n > 1) {
result = replaceNumber(result);
n--;
}
return result[k - 1];
};
// 替换当前值
function replaceNumber(str) {
let arr = str.split("");
return arr.reduce((total, cur) => {
total += cur === "0" ? "01" : "10";
return total;
}, "");
}
复制代码
结果
空间溢出了,说明这道题目拒绝我们使用暴力解法,那么我们需要找规律。
找规律
我们可以直接看前几轮的输出,会发现一个有趣的规律:
通过观察发现当前行的值等于上一行的值 + 上一行的值做取反(即把上一轮的0换成1,1换成0)操作;那么我们可以把这道题目转换为递归问题:我们可以把当前的整个字符串切割,如果当前行的值在左半截,那么它等于上一轮的值,如果它在右半截,那么它等于上一轮的值取反,这样子一层层往上找即可。
/**
* @param {number} n
* @param {number} k
* @return {number}
*/
var kthGrammar = function(n, k) {
// 边界
if (n === 1) return 0;
// 先算出当前总长度, 从1开始每一轮翻两倍
let len = Math.pow(2, n - 1);
// 如果k在左边的一半,直接取值,如果在右边则取反
if (k > len / 2) {
return Number(!kthGrammar(n - 1, k - len / 2));
} else {
return kthGrammar(n - 1, k);
}
};
复制代码
看懂了的小伙伴可以点个关注、咱们下道题目见。如无意外以后文章都会以这种形式,有好的建议欢迎评论区留言。