算法力扣刷题记录 二十三【151.翻转字符串里的单词】

前言

字符串篇,继续。

记录 二十三【151.翻转字符串里的单词】

--

一、题目阅读

给你一个字符串 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"
解释:如果两个单词间有多余的空格,反转后的字符串需要将单词间的空格减少到仅有一个。

提示:

1 <= s.length <= 104
s 包含英文大小写字母、数字和空格 ' '
s 中 至少存在一个 单词

进阶:

如果字符串在你使用的编程语言中是一种可变数据类型,请尝试使用 O(1) 额外空间复杂度的 *原地* 解法。

二、尝试实现

思路

(1)对参数s应该以空格 为分隔符,分割一个一个单词。

(2)流对象能够忽略空格或'\n',虽然读取,但是能够忽略。

用stringstream可以完成以空格分割和忽略读取到的空格。

代码实现(测试通过)

cpp 复制代码
class Solution {
public:
    string reverseWords(string s) {
        string result;	//返回值
        stringstream ss(s);	//处理字符串的流对象定义。
        string str;
        while(ss>>str){	//用>>运算符可以忽略空格,以空格为分隔符
            if(result.empty()){
                result.insert(0,str);
            }else{
                result.insert(0,str+' ');	//在新string
            }  
        }
        return result;
    }
};

进阶

在原有字符串中进行解决:

进阶思路

结合记录二十二的思路,在原有string s的后面扩容,把新倒序的单词续到原有字符串的后面。最后再把前面size长度删掉,只取size之后的。

(1)首先不能覆盖填充,因为单词不能改变,所以覆盖填充会改变原有的单词。所以就把倒序操作放到整个size的后面

(2)扩容多大区间?

  • 统计多少个字母;假设count个字母;
  • 统计一共多少个单词。假设有n个单词,需要n-1个空格。
  • 所以扩容count+n-1。在原有size的后面。
  • 总结:s.resize(size+count+n-1);

(3)接下来,翻转单词顺序。

  • 一个指针i从s[size-1]倒着往前找单词,直到s[i] != ' '。i不再是空格;
  • 单词内的字母没有倒序,所以得找这个单词的头。用指针j=i-1开始,往前找,直到s[j] == ' '等于空格。s[j+1]~s[i]之间是一个单词。
  • 用一个指针k记录扩容后面的位置,要把"s[j+1]~s[i]"这个单词放在哪个位置?k初始是size。从s[size]开始。
  • 空格处理:
    • 如果是倒数第一个单词,那么前面不需要有空格;如果不是倒数第一个单词,就要有空格。

代码实现(测试通过)

可以查看注释解释

cpp 复制代码
class Solution {
public:
        string reverseWords(string s) {
        int size = s.size();
        int count = 0;  //统计有多少个字母
        int num = 0;    //统计多少个单词
        for(int i = 0;i < size;i++){  
            if(s[i] != ' '){	//挨个遍历原来的size,不是空格时,count可以++
                count++;
                if(i+1 < size && s[i+1]==' ' ){	//如果下一位是空格,说明是一个单词,所以num++
                    num++;
                }else if(i == size-1){	//如果s[size-1]不是空格,最后一个单词也要加上。
                    num++;
                }
            }
        }

        s.resize(size+count+num-1);//扩容
        //在扩容的区间开始翻转单词顺序
        int k = size;
        int phase = 0;
        //倒着往前找
        for(int i =size-1;i >=0;){
            if(s[i] != ' '){	//遇到一个单词的末尾字母
                int j = i-1;
                int subchar = 1;
                phase++;		//记录当前翻转的第几个单词
                for(j;j >=0;j--){	//找这个单词多长,for结束后s[j+1]是这个单词的首字母
                    if(s[j] == ' '){
                        break;
                    }
                    subchar++;	//记录单词多长
                }
                if(phase != 1){	//如果不是倒数第一个单词,在后面放单词之前先加一个空格。
                    s[k++] = ' ';
                }
                for(int m = 0;m < subchar;m++){	//单词字母没有倒转,所以正向的顺序把字母放置好。
                    s[k++] = s[j+m+1];
                }
                i = j;
                
            }
            i--;
        }
        s.erase(0,size);	//最后删除从0开始,长度为size的部分。把原有部分删除。
        return s;
    
    }
};

总结:两种方法都解决了。这种扩容可以认为是原地操作。


三、代码随想录学习

学习内容

主要看思路区别和实现区别:

(1)用split库函数,分割string s , 再用一个新字符串。C++里面没有split的函数。要么就是C风格的strtok函数,在< cstring >中,原来的string.h。

(2)因为想到reverse翻转会直接把字母翻转,就没再考虑;其实可以reverse整个string s,再把每个单词reverse回来。

(3)处理空格:多余的空格要删掉,如果不是第一个单词,前面还要留一个空格。(删除空格,也是删除元素,string可以像数组一样操作,所以用双指针法)

代码实现

cpp 复制代码
class Solution {
public:
void phaseReverse(string& s,int start ,int end){	//库函数reverse使用类型iterator。
            if(start > s.size() || end < 0){
                return;
            }
            for(int i = start,j = end-1;i < j;i++,j--){ //左闭右开
                swap(s[i] ,s[j]);
            }
        }
    string reverseWords(string s) {
        //删除空格
        int fast = 0;
        int slow = 0;
        for(;fast < s.size();fast++){
            if(s[fast] != ' '){		//当遇到不该删的字符。如果没有这个判断,单词之间的空格没有删掉。
                if( slow != 0){
                    s[slow++] = ' ';//先给空格再slow++
                }
                while(fast < s.size() && s[fast] != ' '){	//必须有 < s.size() 判断,没有会越界。
                    s[slow] = s[fast];
                    fast++;
                    slow++;
                }
            }
        }

        s.resize(slow);
        reverse(s.begin(),s.end());
        //在翻转每个单词
        for(int i = 0,j = 0;j <= s.size();j++){	//因为phaseReverse左闭右开,所以j <= ,带等号。
            if(s[j] == ' ' || j == s.size()){
                phaseReverse(s,i,j);
                i = j+1;
            }
        }
        return s;
    }
};

总结

(1)用一个新string,用stringstream处理。

(2)扩容原来的string s,倒着往前找单词,放到后面,最后把前面长size的内容erase。

(3)先删除空格(双指针法),再reverse整体,再翻转某个单词。

(欢迎指正,转载表明出处)

相关推荐
LCG元2 小时前
【面试问题】JIT 是什么?和 JVM 什么关系?
面试·职场和发展
唐诺3 小时前
几种广泛使用的 C++ 编译器
c++·编译器
XH华3 小时前
初识C语言之二维数组(下)
c语言·算法
南宫生4 小时前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
不想当程序猿_4 小时前
【蓝桥杯每日一题】求和——前缀和
算法·前缀和·蓝桥杯
落魄君子4 小时前
GA-BP分类-遗传算法(Genetic Algorithm)和反向传播算法(Backpropagation)
算法·分类·数据挖掘
冷眼看人间恩怨4 小时前
【Qt笔记】QDockWidget控件详解
c++·笔记·qt·qdockwidget
菜鸡中的奋斗鸡→挣扎鸡4 小时前
滑动窗口 + 算法复习
数据结构·算法
红龙创客4 小时前
某狐畅游24校招-C++开发岗笔试(单选题)
开发语言·c++
Lenyiin4 小时前
第146场双周赛:统计符合条件长度为3的子数组数目、统计异或值为给定值的路径数目、判断网格图能否被切割成块、唯一中间众数子序列 Ⅰ
c++·算法·leetcode·周赛·lenyiin