一、题目
「外观数列」是一个数位字符串序列,由递归公式定义:
countAndSay(1) = "1"countAndSay(n)是countAndSay(n-1)的行程长度编码。
行程长度编码(RLE)是一种字符串压缩方法,其工作原理是通过将连续相同字符(重复两次或更多次)替换为字符重复次数(运行长度)和字符的串联。例如,要压缩字符串 "3322251" ,我们将 "33" 用 "23" 替换,将 "222" 用 "32" 替换,将 "5" 用 "15" 替换并将 "1" 用 "11" 替换。因此压缩后字符串变为 "23321511"。
给定一个整数 n ,返回 外观数列 的第 n 个元素。
示例 1:
输入: n = 4
输出: "1211"
解释:
countAndSay(1) = "1"
countAndSay(2) = "1" 的行程长度编码 = "11"
countAndSay(3) = "11" 的行程长度编码 = "21"
countAndSay(4) = "21" 的行程长度编码 = "1211"
示例 2:
输入: n = 1
输出: "1"
解释:
这是基本情况。
提示:
1 <= n <= 30
二、解答
题目分析
-
核心概念:本题的关键在于理解 “外观数列” 的定义,它是通过递归的方式,对前一个数列进行行程长度编码得到下一个数列。行程长度编码的操作是将连续相同的字符,用字符出现的次数和该字符本身串联来替换。
-
输入输出:输入是一个整数
n,表示要获取外观数列的第n个元素;输出是一个字符串,即外观数列的第n个元素对应的行程长度编码字符串。 -
约束条件:题目限定了
1 <= n <= 30,这意味着我们在计算外观数列的过程中,最多只需要进行 30 次递归计算行程长度编码,不需要考虑更大规模的计算情况。 -
解题思路:
- 可以通过循环或递归的方式来求解。从基本情况
countAndSay(1) = "1"开始,根据行程长度编码的规则,依次计算出countAndSay(2)、countAndSay(3)直到countAndSay(n)。 - 在实现行程长度编码时,需要遍历字符串,统计连续相同字符的个数,并按照规则生成新的字符串。
- 可以通过循环或递归的方式来求解。从基本情况
示例分析
-
示例 1:
-
输入:
n = 4 -
计算过程:
- 首先,
countAndSay(1) = "1",这是外观数列的起始值。 - 计算
countAndSay(2)时,对countAndSay(1)的值"1"进行行程长度编码。因为"1"中只有 1 个1,所以编码后得到"11"。 - 计算
countAndSay(3)时,对countAndSay(2)的值"11"进行行程长度编码。"11"中有 2 个1,所以编码后得到"21"。 - 计算
countAndSay(4)时,对countAndSay(3)的值"21"进行行程长度编码。"21"中是 1 个2和 1 个1,所以编码后得到"1211"。
- 首先,
-
输出:
"1211"
-
-
示例 2:
- 输入:
n = 1 - 计算过程:根据外观数列的定义,
countAndSay(1) = "1",这是基本情况,不需要对其他字符串进行行程长度编码。 - 输出:
"1"
- 输入:
代码
Java
class Solution {
public String countAndSay(int n) {
String str = "1";
for (int i = 2; i <= n; ++i) {
StringBuilder sb = new StringBuilder();
int start = 0;
int pos = 0;
while (pos < str.length()) {
while (pos < str.length() && str.charAt(pos) == str.charAt(start)) {
pos++;
}
sb.append(Integer.toString(pos - start)).append(str.charAt(start));
start = pos;
}
str = sb.toString();
}
return str;
}
}
python
class Solution:
def countAndSay(self, n: int) -> str:
prev = "1"
for i in range(n-1):
curr = ""
pos = 0
start = 0
while pos < len(prev):
while pos < len(prev) and prev[pos] == prev[start]:
pos += 1
curr += str(pos - start) + prev[start]
start = pos
prev = curr
return prev
代码分析
Java
1. 类和方法定义
java
class Solution {
public String countAndSay(int n) {
class Solution:定义了一个名为Solution的类。public String countAndSay(int n):定义了一个公共方法countAndSay,它接受一个整数参数n,并返回一个字符串。
2. 初始化
java
String str = "1";
- 初始化变量
str为"1",这是外观数列的第一项。
3. 循环生成外观数列
java
for (int i = 2; i <= n; ++i) {
- 使用
for循环从第 2 项开始,直到第n项。
4. 构建当前项
java
StringBuilder sb = new StringBuilder();
int start = 0;
int pos = 0;
StringBuilder sb:用于构建当前项的字符串。start:记录当前连续相同数字的起始位置。pos:用于遍历字符串。
5. 遍历前一项字符串
java
while (pos < str.length()) {
while (pos < str.length() && str.charAt(pos) == str.charAt(start)) {
pos++;
}
sb.append(Integer.toString(pos - start)).append(str.charAt(start));
start = pos;
}
- 外层
while循环确保遍历完前一项字符串。 - 内层
while循环找到连续相同数字的结束位置。 sb.append(Integer.toString(pos - start)).append(str.charAt(start)):将连续相同数字的个数和该数字添加到StringBuilder中。start = pos:更新起始位置。
6. 更新当前项
java
str = sb.toString();
- 将
StringBuilder中的内容转换为字符串,并更新str为当前项。
7. 返回结果
java
return str;
- 返回第
n个外观数列。
python
1. 类和方法定义
python
class Solution:
def countAndSay(self, n: int) -> str:
class Solution:定义了一个名为Solution的类。def countAndSay(self, n: int) -> str:定义了类中的countAndSay方法,它接收一个整数参数n,并返回一个字符串类型的结果。
2. 初始化
python
prev = "1"
- 把变量
prev初始化为"1",这代表外观数列的首项。
3. 循环生成外观数列
python
for i in range(n - 1):
- 利用
for循环从第 2 项开始生成,直至第n项。因为已经将首项初始化为"1",所以只需循环n - 1次。
4. 构建当前项
python
curr = ""
pos = 0
start = 0
curr:用于构建当前要生成的外观数列项。pos:在遍历前一项字符串时,作为当前位置的索引。start:标记当前连续相同数字的起始位置。
5. 遍历前一项字符串
python
while pos < len(prev):
while pos < len(prev) and prev[pos] == prev[start]:
pos += 1
curr += str(pos - start) + prev[start]
start = pos
- 外层
while循环保证遍历完前一项字符串。 - 内层
while循环找出连续相同数字的结束位置。 curr += str(pos - start) + prev[start]:把连续相同数字的数量和该数字组合成字符串添加到curr中。start = pos:更新起始位置,以便继续处理后续数字。
6. 更新当前项
python
prev = curr
- 将
curr赋值给prev,使prev成为下一次循环中的前一项。
7. 返回结果
python
return prev
- 返回第
n个外观数列。
总结
两段代码分别使用 Java 和 Python 实现了相同的功能,即生成第 n 个「外观数列」。「外观数列」是一个从 "1" 开始的整数序列,后续每一项都是对前一项数字分布的描述。
- Java 代码:定义了
Solution类,在countAndSay方法中,使用StringBuilder来构建新的字符串,利用while循环进行字符串遍历和处理。 - Python 代码:同样定义了
Solution类,在countAndSay方法中,使用普通字符串拼接来构建新字符串,借助while循环完成字符串操作。