字符串算法题

目录

题目一——14. 最长公共前缀 - 力扣(LeetCode)

1.1.两两比较

1.2.统一比较 

题目二——5. 最长回文子串 - 力扣(LeetCode) 

2.1.中心拓展算法

题目三——67. 二进制求和 - 力扣(LeetCode) 

 题目四——43. 字符串相乘 - 力扣(LeetCode)


虽然说,我们这里是简单的介绍字符串的题目一般是怎么做,但是不太会深入介绍更多的字符串的算法

题目一——14. 最长公共前缀 - 力扣(LeetCode)

1.1.两两比较

我们可以两两比较得到我们的两个字符串的最长公共前缀,再将这个公共前缀和其他字符串进行再次查询公共前缀

class Solution {
public:
    std::string longestCommonPrefix(std::vector<std::string>& strs) {
        // 如果字符串向量为空,直接返回空字符串
        // 但在这个实现中,我们假设向量至少包含一个字符串
        // 并使用第一个字符串作为初始的公共前缀
        std::string ret = strs[0];
        
        // 遍历字符串向量中的每一个字符串(从第二个开始)
        for (int i = 1; i < strs.size(); i++) {
            // 更新公共前缀为当前公共前缀和当前字符串的公共部分
            ret = findCommon(ret, strs[i]);
            
            // 如果在某个点公共前缀变为空字符串,则无需继续比较,因为空字符串是最短的前缀
            // 但在这个实现中,我们保持循环以展示完整的逻辑
            // 在实际应用中,可以提前退出循环以提高效率
        }
        
        // 返回最终的公共前缀
        return ret;
    }
    
    // 定义一个辅助函数,用于找出两个字符串的公共前缀
    std::string findCommon(std::string& s1, std::string& s2) {
        int i = 0;
        
        // 遍历两个字符串,直到达到其中一个字符串的末尾或发现不匹配的字符
        while (i < std::min(s1.size(), s2.size()) && s1[i] == s2[i]) {
            i++;
        }
        
        // 返回s1中从开头到第一个不匹配字符之前的子串
        // 这个子串就是s1和s2的公共前缀
        return s1.substr(0, i);
    }
};

在C++中,std::string 类的 substr 成员函数用于获取一个字符串的子串。函数原型通常如下:

std::string substr(size_t pos = 0, size_t len = npos) const; 

  • pos 是子串开始的起始位置(基于0的索引)。如果省略,默认为0,即从字符串的开头开始。
  • len 是要提取的字符数。如果省略,或者其值大于从 pos 到字符串末尾的字符数,则 substr 会提取从 pos 开始到字符串末尾的所有字符。

举个例子:

std::string s1 = "hello, world!";
size_t cur = 5;
std::string result = s1.substr(0, cur);
// 此时,result 的值将是 "hello"

在这个例子中,我们从 s1 的开头提取了5个字符,得到了子串 "hello"

1.2.统一比较 

class Solution {
public:
    std::string longestCommonPrefix(std::vector<std::string>& strs) {
        // 如果字符串向量为空,理论上应该返回一个空字符串,
        // 但在这个实现中,我们假设向量至少包含一个字符串,
        // 并且以第一个字符串为基准进行比较。
        
        // 外层循环遍历第一个字符串的每一个字符
        for (int i = 0; i < strs[0].size(); i++) {
            // 获取当前正在比较的字符(来自第一个字符串)
            char tmp = strs[0][i];
            
            // 内层循环遍历除第一个字符串外的其他所有字符串
            for (int j = 1; j < strs.size(); j++) {
                // 检查当前字符索引i是否超出了其他字符串的长度,
                // 或者当前字符在其他字符串中是否与tmp不同。
                if (i == strs[j].size() || tmp != strs[j][i]) {
                    // 如果满足上述任一条件,说明当前字符不是公共前缀的一部分,
                    // 因此返回当前已经确认的公共前缀(即strs[0]的前i个字符)。
                    // 使用substr函数从strs[0]的开头截取到第i个字符(不包含第i个字符)。
                    return strs[0].substr(0, i);
                }
            }
        }
        
        // 如果外层循环顺利结束,说明第一个字符串的所有字符
        // 在其他所有字符串中都找到了匹配,因此第一个字符串本身就是公共前缀。
        return strs[0];
    }
};

题目二——5. 最长回文子串 - 力扣(LeetCode) 

2.1.中心拓展算法

这个其实是借鉴了暴力解法和回文。

枚举每⼀个可能的⼦串⾮常费时,有没有⽐较简单⼀点的⽅法呢?

对于⼀个⼦串⽽⾔,如果它是回⽂串,并且⻓度⼤于2,那么将它⾸尾的两个字⺟去除之后,它仍然是 个回⽂串。

如此这样去除,⼀直除到⻓度⼩于等于2时呢?

  • ⻓度为1的,⾃⾝与⾃⾝就构成回⽂;
  • ⽽⻓度为2的,就要判断这两个字符是否相等了。

从这个性质可以反推出来,从回⽂串的中⼼开始,往左读和往右读也是⼀样的。

那么,是否可以枚举 回⽂串的中⼼呢?

从中⼼向两边扩展,

  • 如果两边的字⺟相同,我们就可以继续扩展;
  • 如果不同,我们就停⽌扩展。

这样 只需要⼀层for循环,我们就可以完成先前两层for循环的⼯作量


中心扩展算法是一种用于寻找最长回文子串的有效方法,它的核心思想是从可能的中心点开始,向两边扩展以检查是否存在回文子串,并记录找到的最长回文子串的位置和长度。

以下是中心扩展算法的具体思想:

  1. 枚举中心点
    • 在一个字符串中,回文子串可以是以单个字符为中心(形成奇数长度的回文子串)或以两个字符之间的空隙为中心(形成偶数长度的回文子串)。
    • 因此,我们需要遍历字符串的每个字符以及每个字符之间的空隙,将它们作为潜在的中心点。
  2. 向两边扩展
    • 对于每个中心点,我们尝试向两边扩展,检查左右两侧的字符是否相等。
    • 如果相等,则继续扩展;如果不相等,则停止扩展。
  3. 记录最长回文子串
    • 在扩展过程中,我们跟踪当前回文子串的长度,并将其与之前找到的最长回文子串的长度进行比较。
    • 如果当前回文子串更长,则更新最长回文子串的起始位置和长度。
  4. 返回结果
    • 遍历完所有中心点后,我们得到了最长回文子串的起始位置和长度。
    • 使用这些信息,我们可以从原始字符串中截取最长回文子串并返回。

在中心扩展算法中,我们区分奇数和偶数长度的回文子串,因为它们的中心点略有不同,并且这会影响我们如何向两边扩展来检查回文。

  1. 奇数长度的回文子串
    • 中心点是一个具体的字符。
    • 我们从这个字符开始,分别向它的左侧和右侧扩展,检查左右两侧的字符是否相等。
    • 例如,在字符串"ababa"中,"aba"是一个奇数长度的回文子串,它的中心点是字符'b'。
  2. 偶数长度的回文子串
    • 中心点位于两个字符之间的空隙。
    • 我们从这两个字符开始,分别向它们的左侧和右侧扩展,同样检查左右两侧的字符是否相等。
    • 例如,在字符串"abba"中,"bb"是一个偶数长度的回文子串,它的中心点是位于'a'和'b'之间的空隙。

class Solution {
public:
    std::string longestPalindrome(std::string s) {
        // 中心扩展算法:通过枚举所有可能的中心点,然后向两边扩展来寻找最长的回文子串
        
        int begin = 0, len = 0; // 用于记录最长回文子串的起始位置和长度
        int n = s.size(); // 字符串的长度
        
        // 遍历字符串,依次枚举所有的中心点(包括字符和字符之间的空隙)
        for (int i = 0; i < n; i++) {
            
            // 奇数长度的回文子串扩展:以当前字符为中心点
            int left = i, right = i; // 初始化左右指针为当前字符的位置
            
            // 向两边扩展,直到字符不相等或达到字符串边界
            while (left >= 0 && right < n && s[left] == s[right]) {
                left--; // 左指针向左移动
                right++; // 右指针向右移动
            }
            
            // 计算当前扩展得到的回文子串的长度
            // 因为left和right在不相等或越界时会停止,这个时候left和right指向的字符都不是回文字符串的一部分
            int currentLen = right - left + 1 - 2;
            
            // 如果当前回文子串长度大于之前记录的最长长度,则更新记录
            if (currentLen > len) {
                begin = left + 1; // 更新最长回文子串的起始位置
                len = currentLen; // 更新最长回文子串的长度
            }
            
            // 偶数长度的回文子串扩展:以当前字符和下一个字符之间的空隙为中心点
            left = i, right = i + 1; // 初始化左右指针为当前字符和下一个字符的位置
            
            // 向两边扩展,直到字符不相等或达到字符串边界
            while (left >= 0 && right < n && s[left] == s[right]) {
                left--; // 左指针向左移动
                right++; // 右指针向右移动
            }
            
            // 同样计算当前扩展得到的回文子串的长度,并更新记录(如果需要)
              // 因为left和right在不相等或越界时会停止,这个时候left和right指向的字符都不是回文字符串的一部分
            currentLen = right - left +1 - 2;
            if (currentLen > len) {
                begin = left + 1; // 更新最长回文子串的起始位置
                len = currentLen; // 更新最长回文子串的长度
            }
        }
        
        // 根据记录的起始位置和长度,从原字符串中截取最长回文子串并返回
        return s.substr(begin, len);
    }
};

题目三——67. 二进制求和 - 力扣(LeetCode) 

 

这道题主要考的主要是考高精度加法。

其实本质上考的是模拟加法的过程,这个思想和2. 两数相加 - 力扣(LeetCode)的是一模一样的。大家可以去这里看算法思想:链表算法题——进阶篇-CSDN博客

class Solution 
{
public:
    string addBinary(string a, string b) 
    {
        string ret; // 初始化结果字符串
        
        // 初始化两个指针,分别指向字符串a和b的末尾
        // 这是因为我们要从字符串的低位(右边)开始相加
        int cur1 = a.size() - 1, cur2 = b.size() - 1, t = 0;
        // t用于存储进位

        // 当两个字符串都还有字符未处理,或者存在进位时,继续循环
        while(cur1 >= 0 || cur2 >= 0 || t)
        {
            // 如果a还有字符,将其对应的字符('0'或'1')转换为整数并加到t上
            if(cur1 >= 0) t += a[cur1--] - '0';
            // 如果b还有字符,同样将其对应的字符转换为整数并加到t上
            if(cur2 >= 0) t += b[cur2--] - '0';
            
            // 将当前位的和(模2)添加到结果字符串中
            // 因为模2的结果只能是0或1,所以这里直接加上'0'字符就可以得到正确的字符表示
            ret += t % 2 + '0';
            
            // 更新进位值,为下一次循环做准备
            t /= 2;
        }
        
        // 因为我们是从低位开始相加的,所以结果字符串是反的
        // 需要将其反转才能得到正确的二进制表示
        reverse(ret.begin(), ret.end());
        
        // 返回相加后的二进制字符串
        return ret;
    }
};

 题目四——43. 字符串相乘 - 力扣(LeetCode)

这道题目又是算乘法的啊!!算法思想其实和上面差不多 

 整体思路就是模拟我们⼩学列竖式计算两个数相乘的过程。但是为了我们书写代码的⽅便性,我们选 择⼀种优化版本的,就是在计算两数相乘的时候,先不考虑进位,等到所有结果计算完毕之后,再去 考虑进位。如下图:

class Solution 
{
public:
    string multiply(string n1, string n2) 
    {
        // 解法概述:首先进行无进位相乘并累加结果,然后处理进位,最后处理可能的前导零
        
        int m = n1.size(), n = n2.size();
        
        // 将两个字符串反转,以便从低位开始相乘,这样可以方便地处理进位
        reverse(n1.begin(), n1.end());
        reverse(n2.begin(), n2.end());
        
        // 创建一个向量来存储每一位相乘的累加结果(包括进位前的结果)
        // 向量长度为m + n - 1,因为两个长度分别为m和n的数相乘的结果长度最多为m + n - 1
        vector<int> tmp(m + n - 1, 0); // 初始化为0,避免未定义行为
        
        // 1. 无进位相乘后相加
        // 遍历n1和n2的每一位,将对应位置的字符转换为整数后相乘,并累加到tmp数组的对应位置上
        for(int i = 0; i < m; i++)
            for(int j = 0; j < n; j++)
                tmp[i + j] += (n1[i] - '0') * (n2[j] - '0');
        
        // 2. 处理进位
        // 初始化当前处理的tmp数组索引和进位值
        int cur = 0, t = 0;
        string ret; // 用于存储最终结果
        
        // 遍历tmp数组,处理每一位的进位
        // 当还有未处理的tmp数组元素或存在进位时,继续循环
        while(cur < m + n - 1 || t)
        {
            // 如果当前索引有效,则加上该索引处的值
            if(cur < m + n - 1) t += tmp[cur++];
            
            // 将当前位的值(模10)转换为字符并添加到结果字符串中
            ret += t % 10 + '0';
            
            // 更新进位值,为下一次循环做准备
            t /= 10;
        }
        
        // 3. 处理前导零
        // 如果结果字符串的最后一个字符是'0',并且结果字符串长度大于1,则删除最后一个字符
        // 这个操作在循环中进行,直到结果字符串不再以'0'结尾或长度变为1
        while(ret.size() > 1 && ret.back() == '0') ret.pop_back();
        
        // 由于之前反转了输入字符串,所以最后需要将结果字符串反转回来
        reverse(ret.begin(), ret.end());
        
        // 返回最终结果字符串
        return ret;
    }
};

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/926296.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

嵌入式Linux - UBoot学习篇

目录 使用tftp上传我们的zImage 在Ubuntu上安装TFTP 把我们的网线连接到Ubuntu上 mmc指令 基本命令 2. 重新扫描和分区管理 3. 硬件分区 4. 启动配置 5. 复位功能和 DSR 配置 关键警告与注意事项&#xff1a; 常见用途&#xff1a; mmc info mmc rescan mmc list …

Ubuntu 20.04 Server版连接Wifi

前言 有时候没有网线口插网线或者摆放电脑位置不够时&#xff0c;需要用Wifi联网。以下记录Wifi联网过程。 环境&#xff1a;Ubuntu 20.04 Server版&#xff0c;无UI界面 以下操作均为root用户&#xff0c;如果是普通用户&#xff0c;请切换到root用户&#xff0c;或者在需要权…

亚马逊自研大语言模型 Olympus 即将亮相,或将在 LLM 竞赛中掀起新波澜

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

采用片上光学相控阵的激光雷达

激光雷达基础知识 LIDAR 基于众所周知的 RADAR 原理雷达是20世纪初就存在的著名技术激光雷达使用光频率而不是无线电波 激光雷达和雷达 使用相控阵的激光雷达通过干涉来提高方向性 激光雷达的输出剖面是阵列因子和单天线远场的乘积。 N &#xff1a;天线数量 k &#xff1a;…

阿里云服务器(centos7.6)部署前后端分离项目(MAC环境)

Jdk17安装部署 下载地址&#xff1a;https://www.oracle.com/java/technologies/downloads/ 选择自己需要的jdk版本进行下载。 通过mac终端scp命令上传下载好的jdk17到服务器的/usr/local目录下 scp -r Downloads/jdk-17.0.13_linux-x64_bin.tar.gz 用户名服务器ip地址:/us…

SQL优化与性能——数据库设计优化

数据库设计优化是提高数据库性能、确保数据一致性和支持业务增长的关键环节。无论是大型企业应用还是小型项目&#xff0c;合理的数据库设计都能够显著提升系统性能、减少冗余数据、优化查询响应时间&#xff0c;并降低维护成本。本章将深入探讨数据库设计中的几个关键技术要点…

41 基于单片机的小车行走加温湿度检测系统

目录 一、主要功能 二、硬件资源 三、程序编程 四、实现现象 一、主要功能 基于51单片机&#xff0c;采样DHT11温湿度传感器检测温湿度&#xff0c;滑动变阻器连接数码转换器模拟电量采集传感器&#xff0c; 电机采样L298N驱动&#xff0c;各项参数通过LCD1602显示&#x…

在VMware虚拟机上安装Kali Linux的详细教程(保姆级教程)

在VMware虚拟机上安装Kali Linux的详细教程 引言 Kali Linux是一个基于Debian的Linux发行版&#xff0c;专为渗透测试和安全审计而设计。它内置了数百种安全工具&#xff0c;广泛应用于网络安全领域。通过在VMware虚拟机上安装Kali Linux&#xff0c;您可以在不影响主操作系统…

30分钟学会正则表达式

正则表达式是对字符串操作的一种逻辑公式&#xff0c;就是用事先定义好的一些特定字符、及这些特定字符的组合&#xff0c;组成一个“规则字符串”&#xff0c;这个“规则字符串”用来表达对字符串的一种过滤逻辑。 作用 匹配 查看一个字符串是否符合正则表达式的语法 搜索 正…

spring-boot-maven-plugin 标红

情况&#xff1a;创建好 Spring Boot 项目后&#xff0c;pom.xml 文件中 spring-boot-maven-plugin 标红。 解决方案&#xff1a;加上 Spring Boot 的版本即可解决。

关于IDE的相关知识之三【插件安装、配置及推荐的意义】

成长路上不孤单&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a; 【14后&#x1f60a;///C爱好者&#x1f60a;///持续分享所学&#x1f60a;///如有需要欢迎收藏转发///&#x1f60a;】 今日分享关于ide插件安装、配置及推荐意义的相关内容…

《通俗易懂 · JSqlParser 解析和构造SQL》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; 希望大家多多支持&#xff0c;后续会继续提升文章质量&#xff0c;绝不滥竽充数&#xff0c;欢迎多多交流…

MySQL底层概述—7.优化原则及慢查询

大纲 1.Explain概述 2.Explain详解 3.索引优化数据准备 4.索引优化原则详解 5.慢查询设置与测试 6.慢查询SQL优化思路 1.Explain概述 使用Explain关键字可以模拟查询优化器来执行SQL查询语句&#xff0c;从而知道MySQL是如何处理SQL语句的&#xff0c;从而分析出查询语句…

从扩散模型开始的生成模型范式演变--SDE

SDE是在分数生成模型的基础上&#xff0c;将加噪过程扩展时连续、无限状态&#xff0c;使得扩散模型的正向、逆向过程通过SDE表示。在前文讲解DDPM后&#xff0c;本文主要讲解SDE扩散模型原理。本文内容主要来自B站Up主deep_thoughts分享视频Score Diffusion Model分数扩散模型…

NeuIPS 2024 | YOCO的高效解码器-解码器架构

该研究提出了一种新的大模型架构&#xff0c;名为YOCO&#xff08;You Only Cache Once&#xff09;&#xff0c;其目的是解决长序列语言模型推理中的内存瓶颈。YOCO通过解码器-解码器结构的创新设计&#xff0c;显著减少推理时的显存占用并提升了长序列的处理效率。 现有大模…

Android 设备使用 Wireshark 工具进行网络抓包

背景 电脑和手机连接同一网络&#xff0c;想使用wireshark抓包工具抓取Android手机网络日志&#xff0c;有以下两种连接方法&#xff1a; Wi-Fi 网络抓包。USB 网络共享抓包。需要USB 数据线将手机连接到电脑&#xff0c;并在开发者模式中启用 USB 网络共享。 查看设备连接信…

腾讯云 AI 代码助手:单元测试应用实践

引言 在软件开发这一充满创造性的领域中&#xff0c;开发人员不仅要构建功能强大的软件&#xff0c;还要确保这些软件的稳定性和可靠性。然而&#xff0c;开发过程中并非所有任务都能激发创造力&#xff0c;有些甚至是重复且乏味的。其中&#xff0c;编写单元测试无疑是最令人…

修改Docker 默认存储目录( Docker Root Dir: /var/lib/docker)

Docker 默认将所有的数据&#xff08;包括镜像、容器、卷等&#xff09;存储在 /var/lib/docker 目录下。这个目录默认被配置在系统的根分区或者较小的分区上。随着容器化应用的增加&#xff0c;或者 Docker 容器和镜像的数量增加&#xff0c;默认存储位置可能会迅速填满&#…

芯片测试-射频中的单位

射频中的单位 &#x1f4a2;dB&#xff0c;dBc&#x1f4a2;&#x1f4a2;dB&#x1f4a2;&#x1f4a2;dBc&#x1f4a2;&#x1f4a2;3dB和0dB&#x1f4a2; &#x1f4a2;dBm和dBw&#x1f4a2;&#x1f4a2;dBuV&#xff0c;dBmV和dBV&#x1f4a2;&#x1f4a2;dBuV&#…

hls视频流学习

hls格式播放的依赖安装&#xff1a; <!-- 新增hls播放库 -->npm install hls.js 组件封装&#xff1a; <template><div class"hls-player-cls"><video ref" video" controls style"width: 100%; max-width: 800px;">…