151.翻转字符串里的单词
题目链接:151.翻转字符串里的单词
讲解视频:字符串复杂操作拿捏了! | LeetCode:151.翻转字符串里的单词
题目描述:
给你一个字符串 s
,请你反转字符串中 单词 的顺序。
单词 是由非空格字符组成的字符串。s
中使用至少一个空格将字符串中的 单词 分隔开。
返回 单词 顺序颠倒且 单词 之间用单个空格连接的结果字符串。
注意:输入字符串 s
中可能会存在前导空格、尾随空格或者单词间的多个空格。返回的结果字符串中,单词间应当仅用单个空格分隔,且不包含任何额外的空格。
示例 1:
输入:s = "the sky is blue
" 输出:"blue is sky the
"
示例 2:
输入:s = " hello world " 输出:"world hello"
解题思路
第一步:使用双指针法去除多余的空格:
- 只有在slow不位于最开头时,才会向slow中手动填空格
- fast指针忽视所有空格,向slow指针位置填值
- 前两步顺序不能变,否则最后一个单词后会多出一个空格
第二步:反转字符串
- 先整体反转字符串
- 再反转各个单词
代码
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指向后缀末尾位置
- 初始化前缀尾j和next数组第0位置
- 前后缀不同的情况下,使用核心思想更新j
- 前后缀相同的情况下,根据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指针