38. 外观数列

题目

「外观数列」是一个数位字符串序列,由递归公式定义:

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 个序列开始,每次根据前一个序列进行行程长度编码。
  • 使用两个字符串 originresult 来存储和构造新的序列。
  • 通过遍历 origin,计算每个字符的重复次数,并将其转换为新的子序列,最终拼接到 result 中。
  • 重复上述过程,直到生成第 n 个序列。

拆解分析

  1. 初始化和基础情况处理
if (n == 1) {
    return strdup("1");
}

当 n 为 1 时,直接返回 “1”。

  1. 初始化两个字符串 originresult
origin = (char*)calloc(5000, sizeof(char));
result = (char*)calloc(5000, sizeof(char));
strcpy(result, "1");

初始化两个字符串,用于存储和构造新的序列。

  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 个序列。

拆解分析

  1. 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;
}

生成给定字符串的下一个行程长度编码字符串。

  1. 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),用于存储生成的序列。

结果

正常

在这里插入图片描述

递归

在这里插入图片描述

  • 9
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值