C++ 栈 模拟 力扣 844. 比较含退格的字符串 题解 每日一题

文章目录

题目描述

题目链接:力扣 844. 比较含退格的字符串

题目描述:

示例 1:

输入:s = "ab#c", t = "ad#c"

输出:true

解释:s 和 t 都会变成 "ac"。
示例 2:

输入:s = "ab##", t = "c#d#"

输出:true

解释:s 和 t 都会变成 ""。
示例 3:

输入:s = "a#c", t = "b"

输出:false

解释:s 会变成 "c",t 仍然是 "b"。
提示:

  1. 1 <= s.length, t.length <= 200
  2. s 和 t 只含有小写字母以及字符 '#'

为什么这道题值得我们花几分钟能懂?

继昨天用栈解决"相邻重复项删除"后,这道题是栈在 "撤销/回退类场景" 的又一经典应用!它不仅延续了"用字符串模拟栈"的核心技巧,还进一步拓展了栈的适用边界------从"消除相邻相同元素"升级到"按规则删除前序元素"。

更关键的是,这道题能帮我们强化两个核心思维:

  1. 问题转化能力:把"带退格的字符串比较"转化为"先处理字符串得到最终形态,再比较",这是算法中"先化简、后求解"的经典思路;
  2. 工具复用能力:昨天刚掌握的"字符串模拟栈"技巧,今天可以直接复用,验证"栈思维"的通用性。

算法原理

这道题的核心是模拟文本编辑器处理退格键的过程,而栈的"后进先出"特性恰好匹配退格键"删除最近输入字符"的逻辑------就像我们日常按"Ctrl+Z"撤销最后一步操作一样。

核心思路

  1. 字符串预处理:分别处理 s 和 t 两个字符串,得到它们经过退格后的最终形态;
  2. 栈模拟编辑过程
    • 遍历字符串的每个字符;
    • 如果当前字符是普通小写字母 → 压入"栈"(用字符串模拟);
    • 如果当前字符是 #(退格键)→ 若"栈"非空,则弹出栈顶字符(删除最近输入的字符),若栈空则无操作;
  3. 结果比较:将两个字符串处理后的结果对比,相等则返回 true,否则返回 false。

为什么还是用字符串模拟栈?

昨天我们已经验证过,相比标准 stack<char>,用字符串模拟栈有两大优势:

  1. 无额外空间开销:字符串本身就是连续内存,无需栈容器的额外控制块;
  2. 操作更高效back()(取栈顶)、pop_back()(弹栈)都是内联操作,无需函数调用开销,且无需最后反转(栈版需要)。

对于这道题,退格操作本质是"删除上一个有效字符",字符串的 pop_back() 正好精准匹配这个需求,逻辑上完全等价于栈,且实现更简洁。

代码实现

cpp 复制代码
class Solution {
public:
    bool backspaceCompare(string s, string t) {
        // 提前判断,减少不必要的处理
        if(s == t)
            return true;
        
        // 分别处理两个字符串,得到退格后的结果
        string s1 = check(s);
        string s2 = check(t);

        // 比较处理后的结果
        return s1 == s2;
    }

    // 核心函数:处理带退格的字符串,返回最终形态
    string check(string s)
    {
        string ret; // 用字符串模拟栈
        for(auto ch : s)
        {
            if(ch != '#')
                ret += ch; // 普通字符,入栈
            else
            {
                if(!ret.empty())
                    ret.pop_back(); // 退格,弹出栈顶(非空时)
            }
        }
        return ret;
    }
};

代码解读

  1. 提前剪枝:先判断 s 和 t 是否完全相等,避免后续无意义的处理;
  2. 复用逻辑 :把字符串处理逻辑封装成 check 函数,代码更简洁、可复用;
  3. 边界处理 :遇到 # 时先判断栈是否为空,避免对空字符串执行 pop_back() 导致越界;
  4. 核心逻辑check 函数中,普通字符入栈,退格字符弹栈(非空时),最终返回的字符串就是处理后的结果。

复杂度分析

维度 分析结果 细节说明
时间复杂度 O(n + m) n 是 s 的长度,m 是 t 的长度;处理每个字符串只需一次遍历,比较结果是 O(min(n,m)),整体为线性时间
空间复杂度 O(n + m) 最坏情况下(无退格键),处理后的字符串需要存储所有字符,空间与原字符串等长

进阶思考(空间优化)

如果想把空间复杂度优化到 O(1),可以采用双指针从后往前遍历的思路:

  • 从字符串末尾开始,统计需要跳过的字符数(遇到 # 则跳过数+1,遇到普通字符且跳过数>0则跳过数-1,否则记录该字符);
  • 同时遍历 s 和 t,对比每一个有效字符,若出现不一致则返回 false。

这种方法虽然空间更优,但逻辑稍复杂,对于本题的输入规模(长度≤200),字符串模拟栈的方式已经足够高效,且代码更易理解、不易出错------算法选择要兼顾性能和可读性

总结

通过这道题,我们进一步巩固了栈在"撤销/回退"类问题中的应用,核心收获:

  1. 栈思维的延伸:从"消除相邻重复"到"删除前序元素",栈的"后进先出"始终适配"最近操作优先处理"的场景;
  2. 代码复用与封装:将重复的字符串处理逻辑封装成函数,提升代码可读性和可维护性;
  3. 问题化简思路:复杂问题(带退格的比较)→ 拆解为两个简单问题(处理字符串 + 比较字符串),降低解题难度。

核心技巧

  • 遇到"退格、撤销、删除最近元素"类问题 → 优先考虑栈;
  • 用字符串模拟栈是处理字符类栈问题的"最优解"(时间/空间常数更小);
  • 写代码时先做简单剪枝(如本题提前判断 s==t),减少无效计算;
  • 边界条件必须处理(如栈空时的退格操作)。

下题预告

我们下次练习 力扣 227. 基本计算器 II ,这是栈在"表达式计算"场景的经典应用------面对包含 +、-、*、/ 的四则运算表达式,如何利用栈处理运算符的优先级?

继"相邻消除""退格撤销"后,栈将帮我们解决更复杂的"优先级处理"问题。表达式计算是面试高频考点,掌握这道题,你对栈的理解会再上一个台阶~

Doro又又又带着小花🌸来啦!🌸奖励🌸看到这里的你!如果这篇「比较含退格的字符串」的博客帮你巩固了栈的应用场景,别忘了点赞收藏~关注我,持续更新算法解题思路,从基础题一步步吃透数据结构!有任何想法或疑问,评论区一起讨论呀~

相关推荐
喵个咪1 小时前
C++ 类型转换:旧风格与四种新风格详解
c++·后端
扶尔魔ocy1 小时前
C/C++ 聊聊结构体、指针、类
c++·qt·
QQ_4376643141 小时前
分布式RPC网络框架
网络·c++·分布式·rpc
fy zs1 小时前
Linux线程互斥与同步
linux·c++
CoderYanger1 小时前
动态规划算法-简单多状态dp问题:14.粉刷房子
开发语言·算法·leetcode·动态规划·1024程序员节
张张努力变强1 小时前
二叉树——精选题目,体验递归的暴力美学!
c语言·数据结构·算法
老王熬夜敲代码1 小时前
万能引用、完美转发
c++·笔记
FMRbpm1 小时前
栈练习--------(LeetCode 739-每日温度)
数据结构·c++·算法·leetcode·新手入门
子一!!1 小时前
数据结构==二叉平衡树,AVL树 ===
数据结构·算法