字符串篇--代码随想录算法训练营第九天| 151.翻转字符串里的单词,卡码网:55.右旋转字符串,28. 实现 strStr(),459.重复的子字符串,字符串总结 ,双指针回顾

151.翻转字符串里的单词

题目链接:151.翻转字符串里的单词

讲解视频:字符串复杂操作拿捏了! | LeetCode:151.翻转字符串里的单词

题目描述:

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

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

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

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

示例 1:

输入:s = "the sky is blue"
输出:"blue is sky the"

示例 2:

输入:s = "  hello world  "
输出:"world hello"

解题思路

第一步:使用双指针法去除多余的空格:

  1. 只有在slow不位于最开头时,才会向slow中手动填空格
  2. fast指针忽视所有空格,向slow指针位置填值
  3. 前两步顺序不能变,否则最后一个单词后会多出一个空格

第二步:反转字符串

  1. 先整体反转字符串
  2. 再反转各个单词

代码

class Solution {
public:
    string reverseWords(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);

        //反转字符串
        for(int i = 0,j = s.size()-1; i<=j; i++,j--)  swap(s[i],s[j]);
        int start = 0;
        for(int i = 0; i < s.size(); i++)
        {
            if(s[i] == ' ')
            {
                reverse(s.begin()+start,s.begin()+i);
                start = i+1;
            }
        }
        reverse(s.begin()+start,s.end());
        return s;
    }
};

卡码网:55.右旋转字符串

题目链接:卡码网:55.右旋转字符串

题目描述

字符串的右旋转操作是把字符串尾部的若干个字符转移到字符串的前面。给定一个字符串 s 和一个正整数 k,请编写一个函数,将字符串中的后面 k 个字符移到字符串的前面,实现字符串的右旋转操作。 

例如,对于输入字符串 "abcdefg" 和整数 2,函数应该将其转换为 "fgabcde"。

解题思路

在原有字符串上进行反转。

先整体反转,再根据整数数值,分两部分再反转

代码

#include<iostream>
#include<string>
#include<algorithm>
using namespace std;

int main()
{
    int n;
    string s;
    cin >> n >> s;
    reverse(s.begin(),s.end());
    reverse(s.begin(),s.begin()+n);
    reverse(s.begin()+n,s.end());
    cout << s << endl;
}

28. 实现 strStr()

题目链接:实现 strStr()

讲解视频:

题目描述

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

示例 1:

输入:haystack = "sadbutsad", needle = "sad"
输出:0
解释:"sad" 在下标 0 和 6 处匹配。
第一个匹配项的下标是 0 ,所以返回 0 。

解题思路

kmp算法核心过程:f位置匹配失败,找f前一位置a,通过a的next值可以确定与末尾为a的后缀相同的最长公共前缀的末尾a位置,从前缀末尾a的后一位置开始(即b)重新匹配。

1、求next数组

next数组里数值含义:记录在每个位置之前(包括当前位置)字符串的最长公共前后缀的长度(不减1),也就是下一次匹配时的初始位置。

  • 前缀是指不包含最后一个字符的所有以第一个字符开头的连续子串;
  • 后缀是指不包含第一个字符的所有以最后一个字符结尾的连续子串

定义两个指针i和j,j指向前缀末尾位置,i指向后缀末尾位置

  1. 初始化前缀尾j和next数组第0位置
  2. 前后缀不同的情况下,使用核心思想更新j
  3. 前后缀相同的情况下,根据next中数值含义更新next数组

2、字符串匹配

当第i位置不匹配时,要对j进行回退,此时要保证i是不能改变的,因此回退要在判断“=”之前

代码

前缀表(不减一)C++实现

class Solution {
public:
    void getNext(vector<int>& next, string needle)
    {
        //第一步:初始化
        int j = 0; //前缀尾。表示最长公共前缀长度
        next[0] = 0;
        for(int i = 1; i < needle.size(); i++)
        {
            //第二步:前后缀不同的情况
            while(j > 0 && needle[i] != needle[j]) j = next[j-1];
            //第三步:前后缀相同的情况
            if(needle[i] == needle[j]) j++;
            next[i] = j;
        }
    }

    int strStr(string haystack, string needle) {
        vector<int> next(needle.size());
        getNext(next,needle);
        for(int i = 0, j = 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 - j + 1;
        }
        return -1;
    }
};

459.重复的子字符串

题目链接:459.重复的子字符串

讲解视频:字符串这么玩,可有点难度! | LeetCode:459.重复的子字符串

题目描述:

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

示例 1:

输入: s = "abab"
输出: true
解释: 可由子串 "ab" 重复两次构成。

示例 2:

输入: s = "aba"
输出: false

解题思路

KMP方法

在由重复子串组成的字符串中,最长相等前后缀不包含的子串就是最小重复子串

只需看最后一个下标的next值,通过判断(总长度len % (总长度len - next[len - 1])== 0)来确定结果。

代码

KMP方法:

class Solution {
public:
    void getNext(vector<int>& next, string s)
    {
        int j = 0;
        next[0] = 0;
        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;
        }
    }

    bool repeatedSubstringPattern(string s) {
        vector<int> next(s.size());
        getNext(next,s);
        int len = s.size();
        if(next[len-1] != 0 && len % (len - next[len-1]) == 0) return true;
        else return false;
    }
};

移动匹配法:

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

字符串总结

双指针回顾总结

 当前已使用过双指针的题目如下:
27. 移除元素 : 原地移除所有数值等于 val 的元素,快慢指针

977.有序数组的平方:按数字平方组成的非递减顺序新数组,i指向起始位置,j指向终止位置

209.长度最小的子数组:滑动窗口--快慢指针

206.反转链表:pre指针和cur指针

19.删除链表的倒数第N个节点:快慢指针

面试题 02.07. 链表相交:两指针交互移动

142.环形链表II:fast一次走两步,slow一次走一步

第15题. 三数之和:i指针+ left,right双指针

第18题. 四数之和:i,k指针 + left,right双指针

实现 strStr():前缀尾j指针和后缀尾i指针

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值