掘金团队号上线,助你 Offer 临门! 点击 查看详情
一、题目描述:
给定一个正整数 n ,输出外观数列的第 n 项。
「外观数列」是一个整数序列,从数字 1 开始,序列中的每一项都是对前一项的描述。
你可以将其视作是由递归公式定义的数字字符串序列:
countAndSay(1) = "1"
countAndSay(n) 是对 countAndSay(n-1) 的描述,然后转换成另一个数字字符串。
前五项如下:
1. 1
2. 11
3. 21
4. 1211
5. 111221
第一项是数字 1
描述前一项,这个数是 1 即 “ 一 个 1 ”,记作 "11"
描述前一项,这个数是 11 即 “ 二 个 1 ” ,记作 "21"
描述前一项,这个数是 21 即 “ 一 个 2 + 一 个 1 ” ,记作 "1211"
描述前一项,这个数是 1211 即 “ 一 个 1 + 一 个 2 + 二 个 1 ” ,记作 "111221"
要 描述 一个数字字符串,首先要将字符串分割为 最小 数量的组,每个组都由连续的最多 相同字符 组成。然后对于每个组,先描述字符的数量,然后描述字符,形成一个描述组。要将描述转换为数字字符串,先将每组中的字符数量用数字替换,再将所有描述组连接起来。
- 示例
示例 1:
输入:n = 1
输出:"1"
解释:这是一个基本样例。
示例 2:
输入:n = 4
输出:"1211"
解释:
countAndSay(1) = "1"
countAndSay(2) = 读 "1" = 一 个 1 = "11"
countAndSay(3) = 读 "11" = 二 个 1 = "21"
countAndSay(4) = 读 "21" = 一 个 2 + 一 个 1 = "12" + "11" = "1211"
提示:
1 <= n <= 30
二、思路分析:
- 感觉有点难度,刚开始也是没写出来,
- 后来参考别人的
- 1.递归+双指针算法
left、right 来确定每个字符的重复数
当 left 和 right 所指字符相同时,right 向右移动一位,直到 right 所指与 left 不同。
确定相同字符的个数,既 right - left 的值,该值表示的即是 left 所指字符的个数。
将结果转为以字符的形式储存。
继续历遍,因为 left 和 right 之间的字符已经历遍过了,所以更新指针 left 至 right。
当 right 超出字符范围时,结束循环。
。
三、AC 代码
/**
* @param {number} n
* @return {string}
*/
var countAndSay = function(n) {
let num = "1";
let sum = ""
for(let l=0;l<n-1;l++){
let sum = ""
for(let i =0,j=0;i<num.length;){
if(num[i]==num[i+j]){
j++;
}else{
sum = sum +j+""+num[i];
i += j;
j=1;
}
}
num = sum;
}
return num;
};
状态:通过
执行用时: 88 ms
内存消耗: 40.6 MB
四、总结
- 下面递归;
/**
* @param {number} n
* @return {string}
*/
var countAndSay = function(n) {
if (n === 1) {
return "1";
};
let pre = countAndSay(n - 1);
let result = "", left = 0, right = 0;
console.log(pre)
while (right < pre.length) {
while (pre[left] === pre[right] && right < pre.length) {
right++;
};
result += (right - left).toString() + pre[left];
left = right;
}
return result;
};
- 也看到用正则的
/**
* @param {number} n
* @return {string}
*/
var countAndSay = function(n) {
var s="1"
while(--n)s=s.replace(/(.)\1*/g,(m,d)=>m.length+d)
return s
};
感觉不是很好理解
仅供学习参考