题目
「外观数列」是一个数位字符串序列,由递归公式定义:
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
代码
完整代码
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
char* countAndSay(int n) {
char* result = NULL;
char* origin = NULL;
if (n == 1) {
return strdup("1");
} else {
origin = (char*)calloc(5000, sizeof(char));
result = (char*)calloc(5000, sizeof(char));
char nums[] = "0123456789";
strcpy(result, "1");
for (int i = 1; i < n; i++) {
strcpy(origin, result);
strcpy(result, "");
int lastNum = origin[0] - '0';
int lastNumCnt = 1;
char new[4] = {'\0'};
for (int j = 1; j < strlen(origin); j++) {
if (nums[lastNum] != origin[j]) {
sprintf(new, "%d%d", lastNumCnt, lastNum);
strcat(result, new);
lastNum = origin[j] - '0';
lastNumCnt = 1;
} else {
lastNumCnt++;
}
}
sprintf(new, "%d%d", lastNumCnt, lastNum);
strcat(result, new);
}
}
free(origin);
return result;
}
思路分析
这套代码用了字符串遍历和拼接的方法。
- 首先,初始化基础情况,即当
n
为 1 时,直接返回 “1”。 - 从第 2 个序列开始,每次根据前一个序列进行行程长度编码。
- 使用两个字符串
origin
和result
来存储和构造新的序列。 - 通过遍历
origin
,计算每个字符的重复次数,并将其转换为新的子序列,最终拼接到result
中。 - 重复上述过程,直到生成第
n
个序列。
拆解分析
- 初始化和基础情况处理
if (n == 1) {
return strdup("1");
}
当 n 为 1 时,直接返回 “1”。
- 初始化两个字符串
origin
和result
origin = (char*)calloc(5000, sizeof(char));
result = (char*)calloc(5000, sizeof(char));
strcpy(result, "1");
初始化两个字符串,用于存储和构造新的序列。
- 生成新的序列
for (int i = 1; i < n; i++) {
strcpy(origin, result);
strcpy(result, "");
int lastNum = origin[0] - '0';
int lastNumCnt = 1;
char new[4] = {'\0'};
for (int j = 1; j < strlen(origin); j++) {
if (nums[lastNum] != origin[j]) {
sprintf(new, "%d%d", lastNumCnt, lastNum);
strcat(result, new);
lastNum = origin[j] - '0';
lastNumCnt = 1;
} else {
lastNumCnt++;
}
}
sprintf(new, "%d%d", lastNumCnt, lastNum);
strcat(result, new);
}
遍历 origin
,计算每个字符的重复次数,并将其转换为新的子序列,最终拼接到 result
中。
复杂度分析
- 时间复杂度:O(n * m),其中
n
为输入整数,m
为每个生成序列的长度。每次生成新的序列需要遍历前一个序列的所有字符。 - 空间复杂度:O(m),用于存储生成的序列。
一题多解
方法2:使用递归的方式
完整代码
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
char nums[] = "0123456789";
char* nextRLE(char* s) {
char* result = (char*)calloc(5000, sizeof(char));
int lastNum = s[0] - '0';
int lastNumCnt = 1;
char new[4] = {'\0'};
for (int j = 1; j < strlen(s); j++) {
if (nums[lastNum] != s[j]) {
sprintf(new, "%d%d", lastNumCnt, lastNum);
strcat(result, new);
lastNum = s[j] - '0';
lastNumCnt = 1;
} else {
lastNumCnt++;
}
}
sprintf(new, "%d%d", lastNumCnt, lastNum);
strcat(result, new);
free(s);
return result;
}
char* countAndSay(int n) {
char* result = strdup("1");
if (n == 1) {
return result;
} else {
for (int i = 1; i < n; i++) {
result = nextRLE(result);
}
}
return result;
}
思路分析
这套代码用了递归和字符串拼接的方法。
- 定义一个辅助函数
nextRLE
,用于生成给定字符串的下一个行程长度编码字符串。 - 在
countAndSay
函数中,从 “1” 开始,逐步调用nextRLE
,直到生成第n
个序列。
拆解分析
nextRLE
函数
char* nextRLE(char* s) {
char* result = (char*)calloc(5000, sizeof(char));
int lastNum = s[0] - '0';
int lastNumCnt = 1;
char new[4] = {'\0'};
for (int j = 1; j < strlen(s); j++) {
if (nums[lastNum] != s[j]) {
sprintf(new, "%d%d", lastNumCnt, lastNum);
strcat(result, new);
lastNum = s[j] - '0';
lastNumCnt = 1;
} else {
lastNumCnt++;
}
}
sprintf(new, "%d%d", lastNumCnt, lastNum);
strcat(result, new);
free(s);
return result;
}
生成给定字符串的下一个行程长度编码字符串。
countAndSay
函数
char* countAndSay(int n) {
char* result = strdup("1");
if (n == 1) {
return result;
} else {
for (int i = 1; i < n; i++) {
result = nextRLE(result);
}
}
return result;
}
逐步调用 nextRLE
函数,直到生成第 n
个序列。
复杂度分析
- 时间复杂度:O(n * m),其中
n
为输入整数,m
为每个生成序列的长度。 - 空间复杂度:O(m),用于存储生成的序列。