内容介绍
「外观数列」是一个数位字符串序列,由递归公式定义:
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
进阶:你能迭代解决该问题吗?
完整代码
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;
}
}
思路详解
问题概述
“计数与说数”问题的定义是:对于给定的整数n
,生成第n
个“计数与说数”序列的字符串。序列是从"1"开始的,每个后续的序列是通过描述前一个序列的数字来生成的。
例如:
- 第1个序列是"1"。
- 第2个序列是"11"(一个1)。
- 第3个序列是"21"(两个1)。
- 第4个序列是"1211"(一个2,一个1)。
- 第5个序列是"111221"(一个1,一个2,两个1)。
代码详解
public String countAndSay(int n) {
// 初始化字符串为序列的第一个元素"1"
String str = "1";
// 从第2个序列开始迭代直到第n个序列
for (int i = 2; i <= n; ++i) {
StringBuilder sb = new StringBuilder(); // 创建一个StringBuilder来构建新的序列
int start = 0; // 用于跟踪当前字符的起始位置
int pos = 0; // 用于遍历字符串
// 遍历当前序列字符串
while (pos < str.length()) {
// 找到连续相同字符的子序列
while (pos < str.length() && str.charAt(pos) == str.charAt(start)) {
pos++;
}
// 计算连续字符的数量,并将其与字符本身添加到StringBuilder中
sb.append(Integer.toString(pos - start)).append(str.charAt(start));
// 更新start为下一个字符的起始位置
start = pos;
}
// 更新str为新生成的序列
str = sb.toString();
}
// 返回第n个序列
return str;
}
关键步骤解释
-
初始化:
str
初始化为"1",这是序列的第一个元素。
-
迭代构建序列:
- 使用一个
for
循环,从第2个序列开始迭代,直到构建出第n
个序列。
- 使用一个
-
StringBuilder的使用:
StringBuilder
用于构建新的序列,因为字符串拼接在Java中是昂贵的操作,而StringBuilder
可以有效地进行字符串的修改。
-
内层while循环:
- 内层
while
循环用于找到当前字符连续出现的子序列,并计算其长度。
- 内层
-
记录和拼接:
- 使用
sb.append(Integer.toString(pos - start))
来记录连续字符的数量。 - 使用
sb.append(str.charAt(start))
来记录字符本身。
- 使用
-
更新start和pos:
- 当找到一个连续字符子序列后,
start
更新为pos
,以便开始寻找下一个子序列。
- 当找到一个连续字符子序列后,
-
更新str:
- 在每次迭代结束时,将
StringBuilder
的内容赋值给str
,以便在下一次迭代中使用。
- 在每次迭代结束时,将
-
返回结果:
- 最终返回构建的第
n
个序列。
- 最终返回构建的第
知识点精炼
-
迭代与递归:
- 代码通过迭代而非递归来生成序列,每次迭代基于前一次的结果。
-
StringBuilder类:
- 使用
StringBuilder
来高效地构建和修改字符串,避免频繁的字符串拼接导致的性能问题。
- 使用
-
字符与整数的转换:
- 使用
Integer.toString()
将整数转换为字符串,以便拼接计数结果。
- 使用
-
指针/索引的使用:
- 通过
start
和pos
两个指针/索引变量来跟踪和比较字符串中的字符。
- 通过
-
字符串遍历:
- 使用
while
循环遍历字符串,找出连续相同的字符子序列。
- 使用
-
子序列计数:
- 通过计算两个指针之间的距离来确定连续字符的数量。
-
状态更新:
- 在每次内层循环结束后,更新
start
指针,为下一次子序列查找做准备。
- 在每次内层循环结束后,更新
-
循环与条件判断:
- 利用嵌套循环和条件判断来识别和处理字符串中的连续字符。
-
结果构建:
- 在每次迭代结束时,将
StringBuilder
的内容转换为字符串,更新为下一次迭代的输入。
- 在每次迭代结束时,将
-
返回值:
- 最终将生成的字符串作为方法的返回值。