字符串题记

news2025/1/16 7:48:40

经典字符串leetcode题

image-20230106201139789

文章目录

  • 经典字符串leetcode题
    • 反转字符串
        • 第一种方法:使用string里的库函数reverse
      • 第二种方法:使用自己写的swap函数
        • 第一种swap函数
        • 第二种swap函数
    • 151.翻转字符串里的单词
    • 28. 实现 strStr()
        • KMP算法
    • 459.重复的子字符串
      • 第一种:暴力法
      • 第二种方法:我把它叫做双倍去头去尾法(移动匹配法)
      • 第三种方法:kmp算法

反转字符串

反转字符串【简单】

第一种方法:使用string里的库函数reverse

给reverse函数传递vector迭代器的begin(第一个有效字符的下标)和end(最后一个有效字符的下标的下一个)—可以知道reverse函数是左开右闭的

class Solution {
public:
    void reverseString(vector<char>& s) {
reverse(s.begin(),s.end());
    }
};

第二种方法:使用自己写的swap函数

第一种swap函数

用i记录第一个元素的位置,j记录最后一个元素的位置,两个位置的元素交换后,i++,j–,两者往中间走

class Solution {
public:
void swap(vector<char>& s)
{
    int i=0,j=s.size()-1;
    while(i<j)
    {
        char tmp=s[i];
        s[i]=s[j];
        s[j]=tmp;
        ++i;
        --j;
    }
}
    void reverseString(vector<char>& s) {
swap(s);
    }
};

第二种swap函数

用按位异或^,按位异或二进制上的值:不同则为1,相同为0;那么利用这个机制两者相互按位异或会出现一个第三值,对这个第三值再次按位异或就能得到对方的值。

class Solution {
public:
void swap(vector<char>& s)
{
    int i=0,j=s.size()-1;
    while(i<j)
    {
       s[i]^=s[j];
       s[j]^=s[i];
       s[i]^=s[j];
       ++i;
       --j;
    }
}
    void reverseString(vector<char>& s) {
swap(s);
    }
};

151.翻转字符串里的单词

翻转字符串里的单词【中等】

思路:第一步:先跳过前面的空格 第二步:把单词依次往前面的位置填,遇到单词中间多余的空格则跳过,最后缩空间为依次一个单词一个空格一个单词这样的字符串第三步:字符串全部逆置第四步:把单词依次逆置回来

第一步和第二步动图:

翻转字符串里的单词.1

class Solution {
public:
void reverse(string&s,int start,int end)
{
    while(start<end)
    {
        swap(s[start++],s[end--]);
    }
}
    string reverseWords(string s) {
int fasti=0;
int slowi=0;
while(s.size()>0&&fasti<s.size()&&s[fasti]==' ')//去除前面的空格
    {
        fasti++;
    }
  for(;fasti<s.size();++fasti)
  {
      if(fasti-1>0&&s[fasti-1]==s[fasti]&&s[fasti]==' ')//遇到多余空格就跳过
  //fasti-1>0是保证前一个位置有空格
  {
continue;
  }
  else{
  s[slowi++]=s[fasti];//把单词往前提
  }  
  }
  if(slowi-1>0&&s[slowi-1]==' ')  //slowi-1>0是保证前一个位置有空格
 {
     s.resize(slowi-1);
 }else
 {
     s.resize(slowi);
 }
int start=0;
int end=s.size()-1;
reverse(s,start,end);//全部逆置
int i=0;
for(;start<=s.size();start++)//把单词逆置回来
{
    if(s[start]==' '||start==s.size())
{
    reverse(s,i,start-1);
    i=start+1;
}
}
return s;
    }
};

28. 实现 strStr()

实现 strStr()【中等】

KMP算法

这题可以用到KMP算法。这里介绍一下KMP算法。KMP算法主要运用在字符串匹配上,当有一个字串串和一个需要匹配的字符串,当子串出现不匹配时,可以根据已经匹配的一部分字符串内容去避免从头去匹配。

这里有一个字符串aabaaf

那么字符串的前缀就是不包括最后一个字符(f)的所有可能字符串:

a,aa,aab,aaba,aabaa。

字符串的后缀相应的就是不包括第一个字符串(a)的所有字符串:

f,af,aaf,baaf,abaaf。

公共前后缀是前后缀相等的部分。

比如字符串a,有前缀就没有后缀,有后缀就没有前缀。那么最大公共前后缀长度是0。

字符串aa,前缀为a,后缀为a,相等前后缀的长度是1

字符串aab,前缀a,aa,后缀b,ab,没有相等前后缀部分。

字符串aaba,前缀a,aa,aab,后缀a,ba,aba,最大公共前后缀长度是1即前缀为a,后缀为a的时候。

好了现在前后缀和公共前后缀及其长度介绍完了。那么现在介绍next数组。next数组就是对应字符串的每个字符对应的最大公共前后缀的长度。那么这些长度组成起来的就是一个前缀表。

image-20230105193947107

假设字串aabaaf要匹配目标串aabaabaaf这个字符串,当遇到子串的f到目标串第二个b的时候,就看f的前一个字符的前缀表的数值。然后回退到该数值对应的字串的下标继续匹配—没错KMP算法就这么简单。(话说我之前遇到这种字符串匹配的时候也想到了回退到前面相等的部分再重新匹配,但是我不知道怎么写,这就是我和大佬之间的区别吧呜呜呜~)

KMP算法通过回退到前面相同的部分再次匹配而不是回退到开头再次匹配,减少了一部分消耗。

KMP算法匹配全过程:

kmp算法演示

接下来就是实现next数组(前缀表),有了前缀表才能实现kmp算法

实现前缀表有三步:初始化,前后缀处理不相等的情况,处理前后缀相等的情况。

第一步初始化:

这里定义两个指针:j指向前缀末尾的位置,i指向后缀末尾的位置

int j=0;
next[0]=j;//一开始公共前后缀长度为0,也就是j的值

第二步:前后缀处理不相等的情况:

那么要看前后缀相不相等,一开始i指向的位置就在j的后一个位置,然后遍历字符串串

for(int i=1;i<s.size();++i)
{
    
}

当遇到s[i]与s[j]不相等的情况,也就是遇到前后缀末尾不相等的情况,就要向前回退

next[j] (前缀表)记录着之前子串相等前后缀的长度

所以处理前后缀不相等情况:

首先j要大于0才能回退,就是最坏情况回退到0的位置,再回退就越界了,j回退到前缀表数值的下标

而且这是个不断回退的过程所以用while

while(j>0&&s[j]!=s[i])
{
j=next[j-1];
}

第三步处理前后缀相等的情况:

前后缀相等,那么指向前缀末尾的指针j就往后一步

if(s[i]==s[j])
{
j++;
}
next[i]=j;

最后整体next数组(前缀表)代码:

void getnext(int *next,const string&s)//开辟好next数组,也把s字符串传进来
{
    int j=0;
    next[0]=j;//初始化
    for(int i=1;i<s.size();++i)
    {
while(j>0&&s[j]!=s[i])//处理前后缀不匹配的情况
{
    j=next[j-1];
}
        if(s[j]==s[i])
        {
              j++;
        }
        next[i]=j;//无论前后缀相等还是不相等,i始终往后走,且前缀表一直在填充
    }
}

next数组演示:

next数组演示

最后使用next数组(前缀表来匹配字符串)

i从0开始,遍历文本串—i指向的是文本串,j指向的是next数组(n[j])

int j=0;
for(int i=0;i<s.size();++i)

接下来是s[i]和n[j]的比较相等

如果s[i]与n[j]不相等,j就回退再重新匹配(回退前缀表的前一个数字对应的字串的下标位置)

while(j>0&&s[i]!=n[j])
{
j=next[j-1];
}

如果s[i]与n[j]相同,那么j++

if(s[i]==n[j])
{
j++;
}

如果j走到next数组的末尾(j==n.size())时,即是文本串里有子串—匹配成功,返回文本串第一个匹配的下标(比如文本串为:aabaabaaf,字串为aabaaf,匹配成功就返回文本串aabaabaaf的为3的下标)

if(j==n.size())
{
return (i-n.size()+1);
}

最后是使用next数组,用字串去匹配文本串的成体代码

int next[n.size()];
//n.size()是子串的大小,用子串的大小去创建next数组
int j=0;
for(int i=1;i<s.size();++i)
{
while(j>0&&s[i]!=n[j])
{
j=next[j-1];
}
    if(s[i]==n[j])
    {
++j;
    }
    if(j==n.size())
    {
return (i-n.size()+1);
    }
}
return -1;//如果不匹配就返回-1

题目代码:

class Solution {
void getnext(int*next,const string&s)
{
    int j=0;
    next[0]=j;
    for(int i=1;i<s.size();++i)
    {
        while(j>0&&s[i]!=s[j])//不相等时
    {
        j=next[j-1];//回退
    }
    if(s[i]==s[j])//相等时
    {
        ++j;
    }
    next[i]=j;
    }
}
public:
    int strStr(string haystack, string needle) {
int next[needle.size()];
int j=0;
getnext(next,needle);
for(int i=0;i<haystack.size();++i)
{
    while(j>0&&haystack[i]!=needle[j])//不匹配时
    {
j=next[j-1];
    }
    if(haystack[i]==needle[j])//匹配时
    {
        ++j;
    }
    if(j==needle.size())//匹配完成时
    {
        return (i-needle.size()+1);
    }
}
return -1;
    }
};

459.重复的子字符串

重复的子字符串【简单】

这里有三种方法

第一种:暴力法

class Solution {
public:
    bool repeatedSubstringPattern(string s) {
        if(s.size()==1)
{
    return false;
}
string tmp="";
string str="";
for(int i=0;i<s.size()/2;++i)//取一半的s就能比较出
{
    tmp+=s[i]; //每次都给tmp字符串增多一个s的字符
    while(str.size()<s.size())
    {
        str+=tmp;//用重复的tmp字符串去和s字符串比较
        if(str.compare(s)==0&&str.size()==s.size())//如果str字符串与s字符串相等且长度也相等就为true
        {
            return true;
        }
       
    }
     str="";//记得要更新str
}
return false;
    }
};

第二种方法:我把它叫做双倍去头去尾法(移动匹配法)

思路:把原字符串加倍后,去头再去尾,看现在的字符串里有没有原字符串,如果有则true,如果没有则为false

image-20230106165238103

image-20230106165556043

class Solution {
public:
    bool repeatedSubstringPattern(string s) {
string tmp=s+s;
tmp.erase(tmp.begin());//去头
tmp.erase(tmp.end()-1);//去尾
if(tmp.find(s)!=std::string::npos)//比较
{
    return true;
}
return false;
    }
};

第三种方法:kmp算法

假设字符串s由多个重复字串构成(重复字串为最小单位),重复字串的长度为x,所以s有n个x组成。

最长相等前后缀长度为m*x,所以n-m=1 【—》推理如下图

如果nx%[(n-m)x]=0,则可以判断出有重复出现的子字符串!

即如果数组的长度正好可以被 (数组长度-最长相等前后缀的长度) 整除 ,说明该字符串有重复的子字符串。

image-20230106195830571

首先要实现next数组(前缀表)然后判断len % (len - (next[len - 1] + 1)) == 0 即可(len为数组长度)

题目代码如下:

class Solution {
    void getnext(int* next,const string&s)
    {
        int j=0;
next[0]=j;
for(int i=1;i<s.size();++i)
{
    while(j>0&&s[i]!=s[j])
    {
        j=next[j-1];
    }
    if(s[i]==s[j])
    {
        j++;
    }
    next[i]=j;
}
    }
public:
    bool repeatedSubstringPattern(string s) {
int next[s.size()];
getnext(next,s);
int len=s.size();
if(next[len-1]!=0&&len%(len-next[len-1])==0)
{
    return true;
}
return false;
    }
};

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

UE4 后期材质节点学习

以下对应的效果&#xff1a; 材质后期在这里进行设置&#xff1a; 在这里调整场景的整体的饱和度 场景的对比度 灰度系数的调整 高光度/图像增益 灰阶偏移的设置 想了解这些专业名词可以看&#xff1a;相机gain lift gamma offset参数意义_baobei0112的博客-CSDN博客_相机gai…

Redis学习笔记(一)Linux下安装部署Redis

一、下载Redis 1、进入官网&#xff0c;进入download页面 https://redis.io/download&#xff0c;找到“List of all releases and hash digests”&#xff0c;点击“ listing of all previous Redis releases on the releases page”&#xff0c;可以进入所有版本下载页面。 …

STM32——DMA直接存储器访问

文章目录一、DMA直接存储器存取DMA简介二、存储器映像三、DMA框图四、DMA基本结构五、DMA请求&#xff08;触发源&#xff09;六、数据宽度与对齐七、存储器到存储器的DMA转运原理图关键代码八、外设到存储器的DMA转运原理图硬件链接图关键代码九、其他一、DMA直接存储器存取 …

【图像分类】4.ResNet残差模块卷积网络的神

ResNet重要性不必多说了吧&#xff0c;论文引用破10w&#xff0c;是我见过最多的&#xff0c;yyds&#xff01; 在对照博主霹雳吧啦和官方pytorch的代码下&#xff0c;手撸一个resnet。如果没有看着代码写&#xff0c;不确定自己是否能根据论文写出model&#xff0c;而且写优雅…

java学习day69(乐友商城)用户注册

今日目标&#xff1a; 创建用户中心 了解面向接口开发方式 实现数据校验功能 实现短信发送功能 实现注册功能 实现根据用户名和密码查询用户功能 1.创建用户中心 用户搜索到自己心仪的商品&#xff0c;接下来就要去购买&#xff0c;但是购买必须先登录。所以接下来我们编…

STM32MP157驱动开发——Linux IIO驱动(下)

STM32MP157驱动开发——Linux IIO驱动&#xff08;下&#xff09;0.前言一、IIO 触发缓冲区1.IIO 触发器2.申请触发器3.释放触发器4.注册触发器5.注销触发器6. IIO 缓冲区7.向驱动程序添加触发缓冲功能8.驱动编写9.触发缓冲测试10.缓冲区读取二、测试App三、测试结果0.前言 上一…

【C++高阶数据结构】B树、B+树、B*树

&#x1f3c6;个人主页&#xff1a;企鹅不叫的博客 ​ &#x1f308;专栏 C语言初阶和进阶C项目Leetcode刷题初阶数据结构与算法C初阶和进阶《深入理解计算机操作系统》《高质量C/C编程》Linux ⭐️ 博主码云gitee链接&#xff1a;代码仓库地址 ⚡若有帮助可以【关注点赞收藏】…

高等数学(第七版)同济大学 习题11-3 (前7题)个人解答

高等数学&#xff08;第七版&#xff09;同济大学 习题11-3&#xff08;前7题&#xff09; 函数作图软件&#xff1a;Mathematica 1.计算下列曲线积分&#xff0c;并验证格林公式的正确性&#xff1a;\begin{aligned}&1. \ 计算下列曲线积分&#xff0c;并验证格林公式的正…

PyTorch深度学习快速入门教程

PyTorch深度学习快速入门教程1、Pytorch加载数据2、Tensorbord的使用3、Transforms的使用4、常见的Transforms5、torchvision中的数据集使用6、DataLoader的使用7、神经网络的基本骨架—nn.module8、卷积操作9、神经网络—卷积层10、神经网络—池化层的使用11、神经网络—非线性…

靴子落地!Mobileye正式启动4D成像雷达量产进程

4D毫米波雷达赛道正在变得越来越拥挤。 在传统雷达时代&#xff0c;全球主要的市场参与者屈指可数&#xff0c;博世、大陆、安波福、海拉等少数几家巨头几乎垄断前装市场。如今&#xff0c;随着4D时代的开启&#xff0c;越来越多的新进入者希望能够实现换道超车&#xff0c;这…

Jar 组件自动化风险监测和升级实践

背景 以 Xstream、Jackson、Fasjson 等为代表的 Jar 组件高危漏洞层出不穷&#xff0c;安全组每年 N 次推动业务线进行第三方 Jar 组件升级&#xff0c;每次升级动辄涉及成百上千个应用服务&#xff0c;给双方都带来了沉重的负担。为了降低安全组在 Jar 组件升级期间的工作量&…

JS 如何利用浏览器的 cookie 保存用户名

前言 浏览器的cookie可以用来存储一些少量的网站信息,比如登录的用户名,用于提高用户体验非常有帮助 有的一些网站在第一次登录后,在指定的时间范围内容,下次在打开网站,再次登录时,不用每次都重新输入用户名的 或在做一些购物车效果时,也可以使用cookie,保持一个状态持续多…

【数据结构与算法——C语言版】3. 二分查找

前言 本文将介绍在线性表查找中非常常用的一种查找算法——二分法&#xff0c;先介绍二分查找法的核心思路&#xff0c;然后进行代码讲解&#xff0c;最终给出二分查找法的时/空复杂度&#xff0c;并比较其和上篇文章【数据结构与算法——C语言版】2. 数组介绍的顺序查找的区别…

神经网络漫谈(一)

神经网络漫谈(一) 发表时间&#xff1a; 2023年1月6日创作地点&#xff1a;湖北省武汉市作者&#xff1a;ixy_com&Bill Kromydas封面图片来源&#xff1a;Towards Data Science 1、背景 基本概念&#xff1a;神经网络&#xff0c;也称为人工神经网络 (ANN) 或模拟神经…

基础数据结构——二叉树

目录 一、二叉树性质 1、满二叉树、完全二叉树 2、平衡二叉树 3、不平衡二叉树 二、二叉树的存储 1、普通做法 2、竞赛做法 三、二叉树的遍历 1、宽度优先遍历 2、深度优先遍历 &#xff08;1&#xff09;先&#xff08;根&#xff09;序遍历 &#xff08;2&#x…

【java中的集合框架】学习接触java中的集合,走上学习数据结构道路

前言&#xff1a; 大家好&#xff0c;我是良辰呀&#x1f3eb;&#x1f3eb;&#x1f3eb;&#xff0c;从今天开始&#xff0c;我们一起来探索数据结构的知识海洋。期待与大家结伴同行&#xff0c;gogogo。&#x1f36c;&#x1f36c;&#x1f36c; &#x1f9d1;个人主页&…

【自学C++】C++命名空间

C命名空间 C命名空间教程 C 中的命名空间实际上就是一个由程序设计者命名的内存区域&#xff0c;程序设计者可以根据需要指定一些有名字的空间域&#xff0c;把一些全局实体分别放在各个命名空间中&#xff0c;从而与其他全局实体分隔开来。 命名空间是 ANSI C 引入的可以由…

前端入门笔记 03 —— Web(html CSS)布局

常用布局 包含两个定义&#xff1a; 尺寸 定位 定义通过CSS拾取网页元素&#xff0c;控制他们控制普通文档流&#xff0c;周边元素&#xff0c;父容器&#xff0c;浏览器窗口 覆盖默认布局行为盒子模型普通文档流 &#xff08;左到右&#xff0c;上到下&#xff09; 块级元素…

2022年中国数字化十大转型趋势

推动数字化发展既是数字时代构筑竞争新优势的战略选择&#xff0c;也是加快构建“双循环”新发展格局和打造高质量发展新引擎的现实需要。我国高度重视数字化发展&#xff0c;不断完善政策措施&#xff0c;着力推动数字化转型。从行业发展看&#xff0c;构建以数据为驱动、以客…

Redis(一)

Nosql 即 Not-Only SQL&#xff08; 泛指非关系型的数据库&#xff09;&#xff0c;作为关系型数据库的补充。 Nosql 作用&#xff1a;应对基于海量用户和海量数据前提下的数据处理问题。 特征 降低磁盘IO次数&#xff0c;越低越好 —— 内存存储 去除数据间关系&#xff…