首页 > 编程语言 >LeetCode算法题 (比较含退格的字符串)Day9!!!C/C++

LeetCode算法题 (比较含退格的字符串)Day9!!!C/C++

时间:2024-12-31 15:27:29浏览次数:8  
标签:字符 Day9 int ret char C++ len 字符串 退格

https://leetcode.cn/problems/backspace-string-compare/description/

一、题目描述

给定 s 和 t 两个字符串,当它们分别被输入到空白的文本编辑器后,如果两者相等,返回 true 。# 代表退格字符。

注意:如果对空文本输入退格字符,文本继续为空。

二、相关知识点了解

        今天要做的这道题目用到的算法,在前几期都有用到过,叫做双指针法,这里就不过多的介绍了,不了解的小伙伴可以点击链接了解一下! 

https://blog.csdn.net/m0_75144071/article/details/144771622?spm=1001.2014.3001.5501 

https://blog.csdn.net/m0_75144071/article/details/143021601?spm=1001.2014.3001.5502

两个题目的难度都不大,看完今天的题解可以自行挑战一下哦!

三、示例分析

示例 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"。

        这里说明了给出两个字符串,其中的‘#’代表退格也就是咱们键盘上的删除键(Backspace),并将其最终的结果进行比对,如果相同返回true,否则返回false。还是比较容易理解的。

四、解题思路&代码实现 

        按照上述的分析,我们可以直接利用一个简单粗暴的方式来实现这段代码。(其中的算法在相关知识点中有解释)就是双指针法。

        我们可以想一下,无非就是不是‘#“号的字符我们留下,是”#’号的字符我们不要。

接下来正式开始今天的代码实现!

bool backspaceCompare(char* s, char* t) {
int len_s = strlen(s);
    // 创建一个字符数组 ret_s 用于存储处理后的字符串 s,其大小为 len_s + 1,以容纳字符串结束符 '\0'
    char ret_s[len_s + 1];
    int j = 0;
    // 遍历字符串 s
    for (int i = 0; i < len_s; i++) {
        // 如果当前字符不是 '#'
        if (s[i]!= '#') {
            // 将当前字符存入 ret_s 数组,并将 j 指针后移一位
            ret_s[j++] = s[i];
        } else {
            // 如果当前字符是 '#',且 j 大于 0(即 ret_s 数组中已有字符)
            if (j > 0) {
                // 将 j 指针回退一位,模拟退格操作
                j--;
            }
        }
    }
    // 在处理后的字符串 ret_s 末尾添加字符串结束符 '\0'
    ret_s[j] = '\0';
}

 这里我们选择先对其中一个字符串进行上述的处理,下一个字符串也同样是这样做。(这里就不再添加注释了,和上述代码进行的操作相同)

bool backspaceCompare(char* s, char* t) {
int len_s = strlen(s);
    // 创建一个字符数组 ret_s 用于存储处理后的字符串 s,其大小为 len_s + 1,以容纳字符串结束符 '\0'
    char ret_s[len_s + 1];
    int j = 0;
    // 遍历字符串 s
    for (int i = 0; i < len_s; i++) {
        // 如果当前字符不是 '#'
        if (s[i]!= '#') {
            // 将当前字符存入 ret_s 数组,并将 j 指针后移一位
            ret_s[j++] = s[i];
        } else {
            // 如果当前字符是 '#',且 j 大于 0(即 ret_s 数组中已有字符)
            if (j > 0) {
                // 将 j 指针回退一位,模拟退格操作
                j--;
            }
        }
    }
    // 在处理后的字符串 ret_s 末尾添加字符串结束符 '\0'
    ret_s[j] = '\0';


    ​
    int len_t = strlen(t);
    char ret_t[len_t+1];
    j = 0;
    for (int i = 0; i < len_t; i++) {
        if (t[i] != '#') {
            ret_t[j++] = t[i];
        } else {
            if (j > 0) {
                j--;
            }
        }
    }
    ret_t[j]='\0';
    return strcmp(ret_s,ret_t)==0;

}

整体的思路就是开辟出一个新的空间用来存放处理后的结果,与另一个结果进行比对。

        但是!在几乎任何的编译语言中都不希望代码出现冗余(也就是重复的部分),我们可以将这段重复的代码,进行封装,使其称为一个独立的函数。在我们需要的时候进行调用即可!

char* result(char* src) {
    // 计算源字符串的长度,并加上1(为字符串结束符'\0'预留空间)
    int len = strlen(src) + 1;
    // 动态分配内存,用于存储处理后的字符串
    // 分配的大小为字符指针大小乘以(长度 + 1)
    // 这里应该是 sizeof(char) * (len),原代码 sizeof(char*) 是错误的
    char* dst = (char*)malloc(sizeof(char) * len);
    if (dst == NULL) {
        // 如果内存分配失败,返回 NULL
        return NULL;
    }
    int j = 0;
    // 遍历源字符串
    for (int i = 0; i < len - 1; i++) {
        // 如果当前字符不是'#'
        if (src[i]!= '#') {
            // 将当前字符存入目标字符串,并将目标字符串索引j后移一位
            dst[j++] = src[i];
        } else {
            // 如果当前字符是'#',且目标字符串已有字符(j > 0)
            if (j > 0) {
                // 将目标字符串索引j回退一位,模拟退格操作
                j--;
            }
        }
    }
    // 在处理后的字符串末尾添加字符串结束符'\0'
    dst[j] = '\0';
    // 返回处理后的字符串指针
    return dst;
}

bool backspaceCompare(char* s, char* t) {
    // 对字符串s进行处理,得到处理后的字符串
    char* ret_s = result(s);
    // 对字符串t进行处理,得到处理后的字符串
    char* ret_t = result(t);
    // 比较两个处理后的字符串是否相等
    bool flag = strcmp(ret_s, ret_t) == 0;
    // 释放动态分配的内存,避免内存泄漏
    free(ret_s);
    free(ret_t);
    // 返回比较结果
    return flag;
}

由于这里需要将处理好的字符串进行回传,所以我们需要用malloc在堆区开辟空间。(注意:使用malloc开辟出的空间,在使用完毕后一定要记得使用free释放掉,避免造成内存泄露。希望大家养成良好的编程习惯)

五、代码优化

        其实上述代码有一定的缺陷,它的空间复杂度为O(N),我们其实完全可以在原地进行对字符串的处理将空间复杂度降低至O(1),而不去选择开辟出一段新的内存空间。可以将其优化成这样

char* result(char* str) {
    // 计算源字符串的长度
    int len = strlen(str);
    int j = 0;
    // 遍历源字符串
    for (int i = 0; i < len; i++) {
        // 如果当前字符不是'#'
        if (str[i]!= '#') {
            // 将当前字符复制到新的位置(实际上是覆盖原字符串前面的部分)
            str[j++] = str[i];
        } else {
            // 如果当前字符是'#',且j大于0(表示前面已经有非'#'字符)
            if (j > 0) {
                // 将j回退一位,模拟退格操作
                j--;
            }
        }
    }
    // 在处理后的字符串末尾添加字符串结束符'\0'
    str[j] = '\0';
    // 返回处理后的字符串指针
    return str;
}

bool backspaceCompare(char* s, char* t) {
    // 分别对字符串s和t进行处理,然后比较处理后的结果
    return strcmp(result(s), result(t)) == 0;
}

        因为这里是直接对源字符串所在的内存空间上进行处理,所以不需要手动开辟空间,直接回传即可!

六、题目总结

  1. 本题通过含‘#’号退格字符进行比较,这一问题考验对字符操作和逻辑判断的掌握。
  2. 双指针法无疑是本题解决问题的关键,希望大家熟练掌握。
  3. 我们在遇见一个题目时,可以先在脑子里形成一段“伪代码”,之后按照这个思路,一步步的进行代码优化(时间复杂度或空间复杂度上)。
  4. 在使用malloc时,务必要记得在使用完毕以后,手动释放掉开辟出的内存,避免造成内存泄露。

        通过这道含退格字符串比较题,深入探索了字符串处理细节与双指针算法精妙之处,从基础的字符判断、指针操作,到复杂的逻辑构建、性能优化,全方位提升编程思维。未来面对类似复杂字符串情境,无论是文本编辑模拟、数据格式清理,都能借鉴本题思路,灵活运用双指针、合理管理内存,写出更高效、健壮的代码,向着编程高手之路稳步迈进。谢谢大家!荆轲刺秦!!!

标签:字符,Day9,int,ret,char,C++,len,字符串,退格
From: https://blog.csdn.net/m0_75144071/article/details/144850188

相关文章

  • 使用libtorch对光度立体算法(photometric stereo)进行加速(C++)
    光度立体法是一种三维重建方法,在一些表面产品的缺陷检测有较多的应用(具有深度的缺陷),但是光度立体法需要对每个像素点都求解一个线性方程组L(n*3)*N(3*1)=I(n*1)(n为光源数),在cpu中计算是非常耗时的。本文借助libtorch,在gpu中通过卷积的方式,实现方程组的求解,耗时大约为cpu......
  • C++背单词
    题目描述:最近小明在学习月份的英文单词一月January二月February三月March四月April五月May六月June七月July八月August九月September十月October十一月November十二月December小明爸爸想测试一下天佑到底有没有学会他说出每个月份的前3个字母,小明就写出月份的......
  • C++书籍推荐
    本人收藏的一些电子版:阅读顺序C++primer基础ProfessionalC++基础+新特性现代C++语言核心特性解析更多新特性,STL并发库介绍C++Templates更多的模板语法STLCookbook现代STL用法并发编程实战深入并发ProgrammingwithC++20Concepts,Coroutines,Ranges,andm......
  • 【c++编程基础】std::unique的理解
    前言项目中想要实现一个功能,对于一个自定义类,包含坐标和类别等属性,按照到某个中心点的角度从小到大排序,如果角度相同,只保留距离中心点更近的元素,过程中用到了0-360的角度计算,自定义函数排序,以及删除重复元素等内容,故记录之。具体内容1.计算到中心点的角度;//计算点到中心点......
  • C++项目链接C语言动态库
     有C++项目B,有C语言动态链接库A,需要在B程序中链接A库。 我们知道C++运行环境可以直接运行C语言程序,但因为C++编译时对方法名的解析不同,所以要在C++项目中运行C语言程序,关键问题是需要告诉C++编译器,按照C语言的规范来编译指定的C代码。上面所述的“指定的C代码”,包括C++项目中......
  • 在 VC++ 里最大化并且前置窗口
    在VC++里最大化并且前置窗口在Windows系统中,如果需要通过编程的方式,前置显示另一个进程的某个窗口,你会发现,你遇到了一个麻烦。至少你会发现,仅仅使用SetForegroundWindow或SetWindowPos是没有效果的。下面是解决方案,在VC++2022、VC++2019,Window10和Wind......
  • 20. C++快速入门--并发基础
    参考:《Professionalc++》,《并发编程实战》1基本概念1.1竞争原子性"原子"(atomic)操作是指一种不可分割的操作,即在执行过程中不会被中断的操作。这种操作要么完全执行,要么完全不执行,不会出现部分执行的情况。应用场景计数器:在多线程环境下安全地递增或递减计数器。标......
  • C++引用
            目录1.引用的声明与初始化2.别名3.引用与指针的区别4.常量引用|const与引用5.引用作为函数参数6.引用返回值7.右值引用(C++11新特性)8.引用的实现        C++中的引用是一种特殊的类型,它是一个别名(或者叫作“引用名”),用来指向另一个变量。......
  • C++:正整数A+B
    正整数A+B题的目标很简单,就是求两个正整数A和B的和,其中A和B都在区间[1,1000]。稍微有点麻烦的是,输入并不保证是两个正整数。输入格式:输入在一行给出A和B,其间以空格分开。问题是A和B不一定是满足要求的正整数,有时候可能是超出范围的数字、负数、带小数点的实数、甚至是一堆乱......
  • 21. C++快速入门--协程 Coroutine 入门
    参考:https://www.cnblogs.com/blizzard8204/p/17563217.htmlhttps://www.bennyhuo.com/2022/03/09/cpp-coroutines-01-intro/本文不完整,更新中1基本概念什么是协程?C++20的协程是一个特殊函数,具有挂起和恢复的能力.(可以不一次性执行)协程可用于异步编程,提供......