Leecode 151.翻转字符串里的单词

题目链接:https://leetcode.cn/problems/reverse-words-in-a-string/description/

题目描述

给你一个字符串 s ,请你反转字符串中 单词 的顺序。

单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。

返回 单词 顺序颠倒且 单词 之间用单个空格连接的结果字符串。

注意:输入字符串 s中可能会存在前导空格、尾随空格或者单词间的多个空格。返回的结果字符串中,单词间应当仅用单个空格分隔,且不包含任何额外的空格。

  • 示例 1:

输入:s = "the sky is blue"

输出:"blue is sky the"

  • 示例 2:

输入:s = " hello world "

输出:"world hello"

解释:反转后的字符串中不能存在前导空格和尾随空格。

  • 示例 3:

输入:s = "a good example"

输出:"example good a"

解释:如果两个单词间有多余的空格,反转后的字符串需要将单词间的空格减少到仅有一个。

解法一 使用栈来进行翻转

本题要求反转字符串中的单词,同时需要将其中多个空格变成一个空格,并且删去最后处于字符串首尾的空格。

  • 首先对于字符串中空格的处理,考虑使用一个指针来扫描遍历整个字符串;

    • 如果遇到空格则需要判断一下这个空格的前一个位置是不是也是空格,就可以判断当前是否是连续多个空格的情况;
    • 需要注意处理字符串首的空格的情况
  • 其次考虑如何将字符串以单词为单位翻转:考虑使用一个栈,将字符串以单词为单位的子字符串入栈,最后再出栈进行拼接
    • 使用一个指针来扫描字符串,遇到非空字符,则将其加到当前单词字符串末尾
    • 如果遇到空格,且上一个位置是空格之外的字符,则将当前一段单词入栈
  • 将字符串按照单词为单位分割入栈之后,再根据栈的“先进后出”的特点,依次出栈进行拼接,并注意使用空字符进行连接,即可得到最终的字符串

根据上面使用栈的思路可以得到代码如下所示:

class Solution {
public:
string reverseWords(string s) {
stack<string> strS; // 初始化一个空的用于存放字符串的栈
int i = 0; // 初始化遍历指针
string word = ""; // 初始化单词字符串
while(s[i] == ' '){i++;} // 处理字符串首连续空格的情况 for(;i < s.size();i++){ // 从头到尾遍历字符串
if(s[i] == ' ' && s[i-1] != ' ') { // 如果遇到空格,且前一个字符不为空格,说明刚遍历完一个单词
strS.push(word); // 则将当前记录的单词字符串入栈
word = ""; // 并清空重置当前单词字符串
}
if(s[i] != ' ')word += s[i]; // 如果当前字符非空格,则添加到单词字符串中
}
string result = word; // 直接用最后一个单词来初始化最后要输出的字符串
while(!strS.empty()){
if(result != "")result += ' '; // 增加判断可以排除当最后一个单词为空的情况,否则会使得最后输出结果的开头多一个空格
result += strS.top(); // 拼接输出的单词
strS.pop(); // 让单词出栈
}
return result; // 最后返回结果字符串
}
};

接下来再上面代码的时间复杂度,首先是将单词逐个压栈,这时候时间复杂度为\(O(n)\),随后再将每个单词字符串逐个出栈并拼接,这时最坏情况也只有\(\frac{n}{2}\)个单词,故时间复杂度也是不大于\(O(n)\),由于这两部分过程是先后执行,而非循环嵌套,因此时间复杂度相加,最终的时间复杂度为\(O(n)\)。同时考虑一下空间复杂度,其中使用了一个额外的字符串变量word和一个字符串栈strS,都是会随着输入的字符串长度的变化的,即空间复杂度为\(O(n)\)。

接下来我们再考虑另一种解法,将空间复杂度降为\(O(1)\)。

解法二 \(O(1)\) 空间复杂度的算法

首先是如何处理字符串中的空格。为了不使用另外一个字符串来存储已删除多余空格的字符串,我们考虑使用快慢指针的方法:

  • 如果快指针指向为字符则将字符复制到慢指针处的字符,并在复制之前先在慢指针处加一个空格
  • 如果快指针遇到空格则不进行操作
  • 重新修改字符串长度

使用这样的方式,快指针遍历完整个字符串之后,即可完成整个字符串中多余空格的删除。我们此处可以先给出这个移除多余空格的函数:

  void removeExtraSpaces(string& s){ // 需要对输出字符进行修改,采用引用的方式输出
int slow = 0; // 初始化慢指针
for(int i = 0; i < s.size(); i++){ // 快指针遍历整个字符串
if(s[i] != ' '){ // 只有当快指针处不为空格时,才进行操作,否则快指针一直往前搜索非空字符
if(slow != 0) s[slow++] = ' '; // 除了一开始时不插入空格,后面每次都需要在慢指针处先添加一个空格
while(i < s.size() && s[i] != ' '){ // 此时快指针已经进入到一个单词内,开始复制,直至字符串结束,或是该单词结束
s[slow++] = s[i++];
}
}
}
s.resize(slow); // 重新更改字符串长度
}

在删去了字符串中所有多余空格后,我们再考虑如何不使用堆栈,如何能够将字符串中的单词翻转。其实只需要先将整个字符串翻转一遍,随后再将各个单词逐个翻转即可。而翻转字符串中一部分内容的操作我们在昨天就已经写过很多次。由此我们可以得到代码如下:

class Solution {
public:
void reverseStr(string& s, int start, int end){ // 翻转字符串中的一个子字符串
for(int i = 0; start + i < end - i; i++){
swap(s[start+i], s[end-i]);
}
} string reverseWords(string s) {
removeExtraSpaces(s); // 先移除多余空格
reverseStr(s,0,s.size()-1); // 翻转整个字符串
int index = 0; // 每个单词的第一个序号
for(int i = 0; i < s.size(); i++){ // 遍历查找每个单词的最后一个序号
if(i == s.size()-1 || s[i+1] == ' '){ // 如果是字符串末尾,或是下一个位置为空格,说明是单词最后一位
reverseStr(s,index,i); // 翻转单词
index = i + 2; // 下一个单词的首字母就在上一个单词的尾字母+2(+1是空格)
}
}
return s;
}
};

使用上面的代码即可在仅使用\(O(1)\)空间复杂度完成本题。

Leecode 28. 找出字符串中第一个匹配项的下标

题目描述

给你两个字符串 haystackneedle ,请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标(下标从 0 开始)。如果 needle 不是 haystack 的一部分,则返回 -1

  • 示例 1:

输入haystack = "sadbutsad", needle = "sad"

输出0

解释"sad" 在下标 06 处匹配。

第一个匹配项的下标是 0 ,所以返回 0

  • 示例 2:

输入:haystack = "leetcode", needle = "leeto"

输出:-1

解释:"leeto" 没有在 "leetcode" 中出现,所以返回 -1

解法1 使用双指针

考虑使用两个指针来对字符串进行遍历,其中一个指针i从头到尾扫过haystack字符串,另一个指针用来遍历字符串haystack中第i个字符开始且与字符串needle相等的部分。即可以这样理解:

  • 当指针i指向haystack中字符与needle中第一个字符不相等时,两个指针ij相等,且一同往后遍历扫过字符串haystack
  • 当指针i指向haystack中字符与needle中第一个字符相等时,指针i暂停,只有指针j继续往后遍历,同时逐个比较字符串needle中第j-i处的字符是否相等
    • 如果有一个字符不相等,则指针j又返回i的位置,并两个指针同时+1继续往后遍历
    • 如果遍历完needle字符串都还没有出现不相同的字符,则说明已经找到了所需要的位置,则返回当前指针i

根据上面算法思想,可以得到下面代码:

class Solution {
public:
int strStr(string haystack, string needle) {
if(needle.size() > haystack.size()) return -1; // 如果字符串needle更长,则直接返回-1;为了避免后续访问越界的情况
for(int i = 0; i < haystack.size() - needle.size() + 1; i++){ // 使用i遍历字符串haystack,如果后续字符长度小于needle长度,说明不存在,故也不需要继续搜索
for(int j = i; needle[j-i] == haystack[j] ; j++){ // 注意初始时j=i;如果当前haystack[i]处字符和needle[0]相等,则暂停i,使用j来遍历比较是否相等
if(j-i == needle.size()-1) return i; // 如果遍历完了字符串needle,说明此时的i就是要找的下标,直接返回即可
}
}
return -1; // 如果遍历完了整个字符串都没找到,则返回-1
}
};

现在分析上面算法的时间复杂度,第一个for循环是遍历数组haystack,最多需要遍历次数haystack.size() - needle.size()次,而第二个for循环相当于比较遍历数组needle,最多也就遍历needle.size()次;总用时大致上为\((m-n)n\)数量级,估计时间复杂度为\(O(m * n)\),其中\(m\)和\(n\)表示两个字符串的长度。

解法2 KMP算法

先挖个坑明天学完KMP再来写,今天休息一下

Leecode 459.重复的子字符串

题目描述

给定一个非空的字符串 s ,检查是否可以通过由它的一个子串重复多次构成。

  • 示例 1:

输入: s = "abab"

输出: true

解释: 可由子串 "ab" 重复两次构成。

  • 示例 2:

输入: s = "aba"

输出: false

  • 示例 3:

输入: s = "abcabcabcabc"

输出: true

解释: 可由子串 "abc" 重复四次构成。 (或子串 "abcabc" 重复两次构成。)

解法1 暴力法求解

要匹配子字符串,考虑遍历可能取到的子字符串长度,长度可以取到从1到字符串的一半,每个长度为i的子字符串(即字符串的前i位字符)逐位与字符串中位置j%i的字符进行比较,如果遍历完整个字符串,同时此时恰好比较完子字符串的最后一位,即j%i==0,就说明该子字符串可以构成整个字符串。因此得到代码如下:

class Solution {
public:
bool repeatedSubstringPattern(string s) {
for(int i = 1; i <= s.size()/2; i++){ // 子字符串长度
int j = 0;
for(; s[j] == s[j%i]; j++){} // 子字符串每次比对的起始位置
if ( j >= s.size() && j%i == 0) return true;
}
return false;
}
};

上面代码的时间复杂度为\(O(n^2)\),因为外层遍历子字符串的长度是从1到\(\frac{n}{2}\),内层遍历需要逐位比较字符串,次数也是在\(O(n)\)数量级。因此两层\(O(n)\)的循环嵌套之后的时间复杂度就为\(O(n^2)\)。

今日总结

今天刷了三道字符串的题,剩下时间主要是在学课内面向对象的知识。学习了内联函数、名空间、深拷贝。刷题涉及到KMP算法准备明天再看,用KMP把这两题的解法2补上。

代码随想录第九天 | Leecode 151.翻转字符串里的单词、Leecode 28. 找出字符串中第一个匹配项的下标、Leecode 459.重复的子字符串的更多相关文章

  1. LeetCode 459. 重复的子字符串(Repeated Substring Pattern)

    459. 重复的子字符串 459. Repeated Substring Pattern 题目描述 给定一个非空的字符串,判断它是否可以由它的一个子串重复多次构成.给定的字符串只含有小写英文字母,并且 ...

  2. Java实现 LeetCode 459 重复的子字符串

    459. 重复的子字符串 给定一个非空的字符串,判断它是否可以由它的一个子串重复多次构成.给定的字符串只含有小写英文字母,并且长度不超过10000. 示例 1: 输入: "abab" ...

  3. Leetcode 459.重复的子字符串

    重复的子字符串 给定一个非空的字符串,判断它是否可以由它的一个子串重复多次构成.给定的字符串只含有小写英文字母,并且长度不超过10000. 示例 1: 输入: "abab" 输出: ...

  4. 代码随想录算法训练营day08 | leetcode 344.反转字符串/541. 反转字符串II / 剑指Offer05.替换空格/151.翻转字符串里的单词/剑指Offer58-II.左旋转字符串

    基础知识 // String -> char[] char[] string=s.toCharArray(); // char[] -> String String.valueOf(str ...

  5. C#版(击败100.00%的提交) - Leetcode 151. 翻转字符串里的单词 - 题解

    版权声明: 本文为博主Bravo Yeung(知乎UserName同名)的原创文章,欲转载请先私信获博主允许,转载时请附上网址 http://blog.csdn.net/lzuacm. C#版 - L ...

  6. LeetCode 151. 翻转字符串里的单词(Reverse Words in a String)

    151. 翻转字符串里的单词 151. Reverse Words in a String

  7. Java实现 LeetCode 151 翻转字符串里的单词

    151. 翻转字符串里的单词 给定一个字符串,逐个翻转字符串中的每个单词. 示例 1: 输入: "the sky is blue" 输出: "blue is sky th ...

  8. 【LeetCode】151. 翻转字符串里的单词(剑指offer 58-I)

    151. 翻转字符串里的单词 知识点:字符串:双指针 题目描述 给你一个字符串 s ,逐个翻转字符串中的所有 单词 . 单词 是由非空格字符组成的字符串.s 中使用至少一个空格将字符串中的 单词 分隔 ...

  9. 【算法训练营day8】LeetCode344. 反转字符串 LeetCode541. 反转字符串II 剑指Offer05. 替换空格 LeetCode151. 翻转字符串里的单词 剑指Offer58-II. 左旋转字符串

    [算法训练营day8]LeetCode344. 反转字符串 LeetCode541. 反转字符串II 剑指Offer05. 替换空格 LeetCode151. 翻转字符串里的单词 剑指Offer58- ...

  10. leetcode python翻转字符串里的单词

    # Leetcode 151 翻转字符串里的单词### 题目描述给定一个字符串,逐个翻转字符串中的每个单词. **示例1:** 输入: "the sky is blue" 输出: ...

随机推荐

  1. Luogu P4310 绝世好题 题解 [ 绿 ] [ 线性 dp ] [ 单调队列优化 ] [ 二进制优化 ]

    题目:绝世好题. 暴力 dp 显然 \(O(n^2)\) 转移即可. 单调队列优化 观察到只有某二进制位两个数都为 \(1\) 时才能转移,因此我们把每个二进制位开一个单调队列,然后对于一个数 \(a ...

  2. 多项式算法再探:FMT 和 FWT

    我们知道,FFT 和 NTT 可以用来解决下面这种问题: \[c_k=\sum_{i+j=k}a_ib_j \] 不过,这并不是卷积的全部形态,比如下面这种: \[c_k=\sum_{i*j=k}a_ ...

  3. ABB机器人本体维修保养方法

    ABB机器人维修保养一般可以参照机器人保养手册里面的描述,这种保养一般分为两种计时方式,一两年内进行一次基础保养或者机器人运行时间不超过10000小时.在对机器人本体进行保养的时候,我们该如何操作呢? ...

  4. 普通人如何靠 AI 副业,1 个月实现月薪 3 万 +

    在物价飞涨.经济低迷的今天,仅靠死工资,却有着不固定的开销?房贷.车贷.孩子的教育费用-- 望着日益增长的开销,你是否也在夜深人静时,为钱包的羞涩而发愁?无数次幻想过拥有一份高收入的副业,却始终在迷茫 ...

  5. 大数据之路Week08_day02 (Flume架构介绍和安装)

    Flume架构介绍和安装 写在前面在学习一门新的技术之前,我们得知道了解这个东西有什么用?我们可以使用它来做些什么呢?简单来说,flume是大数据日志分析中不能缺少的一个组件,既可以使用在流处理中,也 ...

  6. Twain Capabilities属性

    Asynchronous Device Events 异步设备事件 CAP_DEVICEEVENT MSG_SET选择应用程序希望Twain源报告的事件; MSG_RESET返回Twain源的首选设置 ...

  7. JSON驱动的vue可视化表单设计器组件

    form-create-designer 是基于 @form-create/element-ui 实现的表单设计器组件.可以通过拖拽的方式快速创建表单,轻松帮你搞定表单. 源码地址: Github | ...

  8. 什么是CPU?

    当你用手机刷短视频.用电脑玩游戏,或是使用智能手表查看健康数据时,这些设备的核心"大脑"--CPU(中央处理器)正在默默工作.它是现代计算设备的核心,但很多人对它一知半解.今天我们 ...

  9. Win系统重装备忘

    蒙德,致态的盘坏块激增,似乎损坏到了系统文件:屏幕截屏会卡,关机后直接该块硬盘内的文件内容回滚,出现驱动报错要求重启... 然后尝试了DiskGenuis迁移系统,PE模式不能用,热迁移后似乎正常分区 ...

  10. [Qt 基础内容-05] QDialogButtonBox

    QDialogButtonBox 本文主要根据QT官方帮助文档以及日常使用,简单的介绍一下QDialogButtonBox的功能以及使用 文章目录 QDialogButtonBox 简介 信号和槽 基 ...