代码随想录算法训练营第9天 | 151.翻转字符串里的单词、卡码网55.右旋转字符串、28.实现strStr()、459.重复的子字符串
文章目录
151.翻转字符串里的单词
解题思路
- 移除多余空格
- 将整个字符串翻转
- 将单个单词翻转
代码实现
class Solution {
public:
void reverse(string& s, int start, int end) {
for (int i = start, j = end; i < j; i++, j--) {
swap(s[i], s[j]);
}
}
void removeExtraSpaces(string& s) {
int slow = 0, fast = 0;
// 去掉字符串前面空格
while (s.size() > 0 && fast < s.size() && s[fast] == ' ') {
fast++;
}
// 去掉字符串中间部分空格
for (; fast < s.size(); fast++) {
if (fast - 1 > 0 && s[fast - 1] == s[fast] && s[fast] == ' ') {
continue;
} else {
s[slow++] = s[fast];
}
}
// 去掉字符串末尾空格
if (slow - 1 > 0 && s[slow - 1] == ' ') {
s.resize(slow - 1);
} else {
s.resize(slow);
}
}
string reverseWords(string s) {
removeExtraSpaces(s);
reverse(s, 0, s.size() - 1);
int start = 0; // 反转单词在字符串中起始位置
int end = 0; // 反转单词在字符串中终止位置
bool entry = false; // 判断是否到单词区间
for (int i = 0; i < s.size(); i++) {
if ((!entry) || (s[i] != ' ' && s[i - 1] == ' ')) {
start = i;
entry = true;
}
if (entry && s[i] == ' ' && s[i - 1] != ' ') {
end = i - 1;
entry = false;
reverse(s, start, end);
}
if (entry && ((i == s.size() - 1)) && s[i] != ' ') {
end = i;
entry = false;
reverse(s, start, end);
}
}
return s;
}
};
题目总结
这道题考察了字符串的多项操作,对于反转字符串,应当要想到使用双指针,本题还多了对于多余空格的删除操作。还有在反转单个单词时可以定义一个 entry 记录是否到达了该要反转单词的区间。
卡码网55.右旋转字符串
解题思路
首先根据输入的 n,将字符串分割成两部分,一部分长度为n,另一部分长度为 数组长度-n,即length-n。
接着将整个字符串反转
最后分别反转两部分子串
代码实现
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
int n;
string s;
cin>>n;
cin>>s;
reverse(s.begin(),s.end());
reverse(s.begin(),s.begin()+n);
reverse(s.begin()+n,s.end());
std::cout << s << std::endl;
}
题目总结
将一个数组转成 set,另一个数组用来遍历,将交集元素存入新 set。
28.实现strStr()
解题思路
直接用KMP算法来做,具体可以看KMP算法讲解。KMP算法主要用在字符串匹配上,当出现字符串不匹配时,能够知道一部分之前已经匹配的文本信息,利用这些信息可以避免从头再去做匹配。
代码实现
class Solution {
public:
void getNext(int* next, const 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;
}
}
int strStr(string haystack, string needle) {
if (needle.size() == 0) {
return 0;
}
int next[needle.size()];
getNext(next, needle);
int j = 0;
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;
}
};
题目总结
KMP算法:首先构造next数组,用来回退。接着遍历文本串,遇到不符合的就回退 j (前缀长度)到相应的位置,符合就 j++,若 j 的值已经达到字符串的长度,就说明验证完毕
459.重复的子字符串
解题思路
和上题思路一致,也是用KMP算法求解
代码实现
class Solution {
public:
void getNext(int* next, const 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) {
if (s.size() == 0) {
return false;
}
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;
}
};
题目总结
注意数组长度减去最长相同前后缀的长度相当于是第一个周期的长度,也就是一个周期的长度,如果这个周期可以被整除,就说明整个数组就是这个周期的循环。