当前位置: 首页 > news >正文

C++ 面试基础考点 模拟题 力扣 38. 外观数列 题解 每日一题

文章目录

  • 题目描述
  • 题目解析
  • 为什么这道题值得你花几分钟看完?
  • 算法原理
  • 代码实现
    • 递归实现
    • 迭代实现
    • 优缺点与时间复杂度分析
  • 总结
  • 下题预告

在这里插入图片描述
在这里插入图片描述

题目描述

题目链接:力扣 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
进阶:能不能不用递归,用迭代解决该问题吗?(毕竟 n 最大才 30,迭代反而更省内存)

题目解析

刚读完题目一定会觉得这力扣叽里咕噜在那里说什么呢?其实这道题不难,关键是别被术语吓到——把它当成“用字符串描述上一个字符串”的游戏,一步步拆解,代码自然就出来了。
在这里插入图片描述
其实外观阵列就是countAndSay(n)是对countAndSay(n-1)的描述,其实本质就是“后一项描述前一项”:
咱们拿前 5 项串起来看,瞬间就能懂:

1 
↓
11(1个1)
↓
21(2个1)
↓
1211(1个2、1个1)
↓
111221(1个1、1个2、2个1)  

每一项都在“翻译”上一项的“长相”——就像你看到“21”,不会说“数字2和1”,而是说“1个2,接着1个1”,文字描述比较单调,看下图我们就能很轻松的理解题目的意思👇
在这里插入图片描述

为什么这道题值得你花几分钟看完?

这道题看似是“简单模拟题”,却是刷算法、备面试的“性价比之王”,花几分钟吃透它,收获远不止“会做一道题”。

1. 基础能力的“试金石”,面试高频且实用
它是校招初面、社招基础轮的“常客”,核心考察**“逻辑拆解+字符串操作+循环/递归实现”** 这三项程序员必备基础能力。面试官不用靠复杂算法,就能快速判断你是否能把“说得出的逻辑”转化为“无bug的代码”——比如会不会处理双指针边界、字符串拼接细节、递归终止条件,这些都是日常开发中高频用到的能力,掌握它相当于夯实了编程基本功。

2. 一题多解,练透“思维灵活性”
它的解法天然分“递归”和“迭代”两种,且各有优劣:

  • 递归版思路直白,3分钟就能写出来,适合快速理清逻辑;
  • 迭代版无栈开销、效率更高,能体现你的“工程优化意识”。
    很多面试官会先让你写递归,再追问“能不能改迭代?”“两种实现哪个更优?”,通过这种“一题多问”,考察你是否会从“能实现”向“实现好”进阶,这种思维在工作中解决复杂问题时也至关重要。

3. 踩坑点典型,帮你规避“低级错误”
在写这道题的时候很容易在这道题的细节上栽跟头,比如:

  • 把“个数(整数)”直接拼接到字符串里,忘了转成字符;
  • 双指针处理时,忽略“right遍历到字符串末尾”的边界;
  • 递归时漏写base case(n=1返回“1”)。
    这些都是编程中常见的“低级错误”,提前在这道题里踩一遍坑,理解错误原因,后续写代码时会更严谨,避免在面试或工作中因细节丢分。

4. 举一反三,覆盖一类“模拟题”
这道题的核心逻辑——“按规则遍历生成下一项”,其实是一大类“模拟类题目”的缩影。比如LeetCode里的“字符串压缩”“杨辉三角”“螺旋矩阵”等题,本质都是“理解规则→模拟过程→处理边界”,吃透这道题的解题思路,再遇到同类题目,就能快速找到突破口,相当于掌握了“一类题的解法模板”。

简单说,这道题是“投入少、产出高”的典型——花几分钟理清逻辑、写通代码,既能应对面试高频考点,又能夯实基础、规避错误,还能举一反三,性价比远超刷一道偏题、难题。

算法原理

解题的核心逻辑是“模拟推导过程”,分两步走:先实现“描述单个字符串”,再实现“从 1 推到 n”。

1.如何描述一个字符串
描述的关键是“数清楚连续相同字符的个数”,用 双指针 最直观,咱们拿例子“1112223”拆解:

  1. 初始化两个指针:left 指向当前要数的字符(初始为 0),right 用来找“连续相同字符的边界”(初始也为 0);
  2. 移动 right:只要 right 没超出字符串长度,且 s[right] == s[left],就一直右移(比如“111”,right 会从 0 移到 3);
  3. 计算个数:当 right 停下来时,“连续相同字符的个数”就是 right - left(比如 3 - 0 = 3,即 3 个 1);
  4. 拼接结果:把“个数”(转成字符)和“当前字符 s[left]”拼起来(3 和 1 → “31”);
  5. 更新 left:让 left 跳到 right 的位置,开始数下一组字符(比如 left 从 0 变 3,接下来数“222”);
  6. 重复以上步骤,直到 left 走到字符串末尾,最终得到的就是描述结果(“1112223” → “313213”)。

详细可以参考下图👇:
在这里插入图片描述
2.从 1 推导到 n(遍历过程)
不管用递归还是迭代,本质都是“一步步推导”:

  • 递归:要算 countAndSay(n),先算 countAndSay(n-1),再描述它;base case 是 n=1 时返回“1”。
  • 迭代:从 base case“1”开始,循环 n-1 次(因为第 1 项已经有了),每次循环都用“描述函数”生成下一项,覆盖当前项,直到循环结束。

比如 n=4,迭代过程就是:
初始值 curr = “1”(第 1 项)→ 第 1 次循环(算第 2 项):描述 curr 得 “11” → 第 2 次循环(算第 3 项):描述 “11” 得 “21” → 第 3 次循环(算第 4 项):描述 “21” 得 “1211” → 循环结束,返回 curr。

代码实现

递归实现

递归的核心是“自顶向下”,先拆分子问题,再合并结果。

#include <string>
using namespace std;class Solution {
public:string countAndSay(int n) {// base case:第1项直接返回"1"if (n == 1) {return "1";}// 先获取前一项的结果string prev = countAndSay(n - 1);string res;  // 存储当前项的结果int left = 0;  // 左指针:指向当前要计数的字符int len = prev.size();while (left < len) {int right = left;  // 右指针:寻找连续相同字符的边界// 移动右指针,直到遇到不同字符或超出范围while (right < len && prev[right] == prev[left]) {right++;}// 拼接:个数(转成字符) + 当前字符res += to_string(right - left);  // 个数转字符串res += prev[left];// 左指针跳到下一组字符的起点left = right;}return res;}
};

迭代实现

迭代是“自底向上”,从基础项开始,循环生成下一项,避免了递归的栈开销。

#include <string>
using namespace std;class Solution {
public:string countAndSay(int n) {string curr = "1";  // 初始值:第1项// 循环n-1次,生成第2到第n项for (int i = 2; i <= n; i++) {string next;  // 存储下一项的结果int left = 0;int len = curr.size();while (left < len) {int right = left;// 找到连续相同字符的边界while (right < len && curr[right] == curr[left]) {right++;}// 拼接:个数 + 字符next += to_string(right - left);next += curr[left];left = right;}curr = next;  // 更新当前项为下一项}return curr;}
};

优缺点与时间复杂度分析

我们用表格对比递归和迭代两种实现:

实现方式优点缺点时间复杂度空间复杂度
递归思路直观,代码结构清晰递归深度最大为30(n=30),虽不会栈溢出,但存在栈空间开销O(2ⁿ)O(2ⁿ)(栈开销 + 字符串存储)
迭代无栈开销,内存占用更稳定,实际运行效率更高需手动循环控制推导过程,对新手稍不直观O(2ⁿ)O(2ⁿ)(仅字符串存储)

时间复杂度解释:外观数列的第n项长度约为2ⁿ(每一项长度大致是前一项的2倍),每次描述字符串都要遍历其所有字符,总时间为O(2¹ + 2² + … + 2ⁿ) = O(2ⁿ)。
空间复杂度解释:主要是存储当前项的字符串,长度约为2ⁿ,因此空间复杂度为O(2ⁿ)。

总结

这道题的核心是“理解规则 + 模拟过程”:

  1. 先吃透“后项描述前项”的规则,也就是RLE编码的逻辑;
  2. 推荐用迭代实现,避免递归的栈开销,且字符串拼接用+=操作效率较高;
  3. 双指针是处理“连续字符计数”的利器,掌握这种技巧能轻松解决类似的字符串压缩问题。

如果需要进一步优化,可以考虑 打表(bushi) 在字符串拼接时预分配内存(比如res.reserve(2 * len)),但对于n≤30的场景,当前实现已经足够高效。

下题预告

下一题咱们一起研究 力扣 1419. 数青蛙。

它是一道有趣的“字符串模拟题”:给定一个由 ‘c’、‘r’、‘o’、‘a’、‘k’ 组成的字符串,每个字符依次代表青蛙“叫一声”的不同阶段(完整叫声为 “croak”),且一只青蛙叫完一声后才能开始下一声。

咱们一起拆解“青蛙叫声阶段匹配”与“数量统计”的核心代码,我们可以先试试思考:比如字符串 “croakcroak” 和 “ccroakroak” 分别需要多少只青蛙,提前感受字符顺序与资源复用的逻辑~

如果这篇外观数列的解析帮你理清了思路,别忘了点赞支持一下呀!这样不仅能让更多需要的朋友看到,也能给我继续拆解算法题的动力~若想跟着节奏攻克下一道数青蛙,记得关注我,后续更新会第一时间提醒你!觉得内容实用的话,还可以顺手收藏,万一以后复习算法时想回顾模拟题规律,打开就能看,省时又高效~
在这里插入图片描述

http://www.dtcms.com/a/498972.html

相关文章:

  • 辽阳企业网站建设费用企业推广软文
  • 天津实体店网站建设深圳宝安区住建局官网
  • shell编程语言---sed
  • iframe实战:跨域通信与安全隔离
  • 购物网站的建设意义html可视化编辑软件
  • Bootstrap 字体图标
  • PVE 9.0 定制 Debian 13 镜像 支持 Cloud-Init 快速部署虚拟机【模板篇】
  • 长春建站模板搭建高端品牌包包都有哪些
  • ai周公解梦抖音快手微信小程序看广告流量主开源
  • 【无标题】大模型-高效优化技术全景解析:微调 量化 剪枝 梯度裁剪与蒸馏 下
  • 自动化信息交付:深度解析AI驱动的每日简报系统架构与实现
  • 做微信公众号第三网站男女做视频观看网站
  • 定时任务Quartz原理详解
  • Rethinking SSIM-Based Optimization in Neural Field Training
  • rocketmq和kafka的区别之顺序消费
  • 套路有*道龙激光-乐多刀销*游戏程序系统方案
  • Angular 2 数据显示
  • 如何快速做单页面网站怎么查网站建设是哪家公司
  • 外国网站备案网站板块设置
  • 从 ClickHouse 到 StarRocks 存算分离: 携程 UBT 架构升级实践
  • 云手机 三角洲行动跑刀
  • Java 反射机制深度解析:从对象创建到私有成员操作
  • c++|表达最值的更好方法|clamp
  • Altium Designer(AD24)File文件功能总结
  • 【EE初阶 - 网络原理】应用层协议(下)
  • Pyinstaller - Python桌面应用打包的首选工具
  • PHP编程语言选择
  • 太原市做网站专业团队广告语
  • 桂林设计单位资质升级网站手机怎么建网站
  • k8s问题详解1:k8s集群上传文件过大导致413 Request Entity Too Large(请求文件实体过大)