代码随想录16 Leetcode151.反转字符串里的单词

发布于:2025-02-11 ⋅ 阅读:(56) ⋅ 点赞:(0)

给你一个字符串 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.移除元素.html
        for (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. 前缀函数的计算:前缀函数用于描述模式串的自身重复结构。它记录了模式串的前后缀的最大匹配长度。
  2. 模式串匹配:在文本串中查找模式串的出现位置,并通过前缀函数进行跳跃,避免不必要的字符比较。

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;
}