给你一个字符串 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: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; //整体思想参考https://programmercarl.com/0027.移除元素.htmlfor (int i = 0; i < s.size(); ++i) { //if (s[i] != ' ') { //遇到非空格就处理,即删除所有空格。if (slow != 0) s[slow++] = ' '; //手动控制空格,给单词之间添加空格。slow != 0说明不是第一个单词,需要在单词前添加空格。while (i < s.size() && s[i] != ' ') { //补上该单词,遇到空格说明单词结束。s[slow++] = s[i++];}}}s.resize(slow); //slow的大小即为去除多余空格后的大小。}string reverseWords(string s) {removeExtraSpaces(s); //去除多余空格,保证单词之间之只有一个空格,且字符串首尾没空格。reverse(s, 0, s.size() - 1);int start = 0; //removeExtraSpaces后保证第一个单词的开始下标一定是0。for (int i = 0; i <= s.size(); ++i) {if (i == s.size() || s[i] == ' ') { //到达空格或者串尾,说明一个单词结束。进行翻转。reverse(s, start, i - 1); //翻转,注意是左闭右闭 []的翻转。start = i + 1; //更新下一个单词的开始下标start}}return s;}
};
KMP算法:
1.暴力算法 时间复杂度是O(m*n)
2.但是KMP算法只需要O(m+n)即可实现
KMP算法(Knuth-Morris-Pratt Algorithm)是一种高效的字符串匹配算法,它通过利用已经部分匹配的结果,避免了重复的比较。与朴素的字符串匹配算法相比,KMP能够在O(n + m)的时间复杂度内完成字符串匹配,其中n是文本串的长度,m是模式串的长度。
KMP算法的核心思想:
KMP算法通过 部分匹配表(通常称为 前缀函数 或 next数组)来减少匹配过程中的重复比较。当发生不匹配时,KMP算法根据部分匹配表跳过一些无意义的比较,从而提高效率。
KMP算法的两个步骤:
- 前缀函数的计算:前缀函数用于描述模式串的自身重复结构。它记录了模式串的前后缀的最大匹配长度。
- 模式串匹配:在文本串中查找模式串的出现位置,并通过前缀函数进行跳跃,避免不必要的字符比较。
1. **前缀函数(next数组)**的构建
前缀函数是一个数组,next[i]
表示模式串中前i个字符的最长公共前后缀的长度。例如,如果模式串是"ABAB"
,那么:
next[0] = 0
(没有前缀和后缀)。next[1] = 0
(字符"A"没有匹配的前后缀)。next[2] = 1
("AB"的最长前后缀是"A")。next[3] = 2
("ABA"的最长前后缀是"AB")。
这个数组的作用是,当遇到不匹配时,模式串不需要从头开始重新匹配,而是跳过一部分已经匹配的内容。
2. KMP算法匹配过程
在匹配过程中,如果文本串的当前字符与模式串的当前字符不匹配,就可以利用next
数组提供的信息跳到一个新的位置,避免从头开始进行不必要的比较。
#include <iostream>
#include <vector>
using namespace std;// 计算模式串的前缀函数(next数组)
void computeNextArray(const string& pattern, vector<int>& next) {int m = pattern.length();next[0] = -1; // 初始化next数组的第一个元素int j = -1;for (int i = 1; i < m; i++) {// 当模式串当前字符与之前的字符不匹配时,根据next数组回退while (j >= 0 && pattern[i] != pattern[j + 1]) {j = next[j]; // 回退到上一个可能的前缀位置}// 如果匹配,扩展匹配长度if (pattern[i] == pattern[j + 1]) {j++;}next[i] = j; // 将当前字符的最长前后缀长度记录到next数组}
}// KMP模式匹配算法
int KMP(const string& text, const string& pattern) {int n = text.length(); // 文本串长度int m = pattern.length(); // 模式串长度// 计算模式串的前缀函数vector<int> next(m);computeNextArray(pattern, next);int i = 0; // 文本串指针int j = 0; // 模式串指针while (i < n) {// 当文本串和模式串的当前字符匹配时,两个指针都前进if (text[i] == pattern[j]) {i++;j++;} else {// 当不匹配时,利用next数组跳过不必要的比较if (j > 0) {j = next[j - 1]; // 根据next数组更新模式串的指针} else {i++; // 如果模式串的第一个字符就不匹配,文本串前进}}// 如果模式串全部匹配完,则返回匹配的位置if (j == m) {return i - j; // 返回模式串在文本中的起始位置}}return -1; // 如果没有找到匹配,返回-1
}int main() {string text = "ABABDABACDABABCABAB"; // 文本串string pattern = "ABABCABAB"; // 模式串// 调用KMP算法进行匹配int index = KMP(text, pattern);if (index != -1) {cout << "Pattern found at index " << index << endl; // 找到匹配时输出位置} else {cout << "Pattern not found." << endl; // 没有找到匹配时输出}return 0;
}