11.13 LeetCode 题目汇总与解题思路

28. 找出字符串中第一个匹配项的下标

1.1 暴力匹配法(Brute Force)

复制代码
class Solution {
public:
    int strStr(string haystack, string needle) {
        int n = haystack.size(), m = needle.size();
        for (int i = 0; i + m <= n; i++) {
            bool flag = true;
            for (int j = 0; j < m; j++) {
                if (haystack[i + j] != needle[j]) {
                    flag = false;
                    break;
                }
            }
            if (flag) {
                return i;
            }
        }
        return -1;
    }
};

特点

  • 时间复杂度:O(n×m)
  • 空间复杂度:O(1)
  • 简单直观,但效率低

1.2 KMP算法(无哨兵版 - 推荐)

复制代码
class Solution {
public:
    int strStr(string haystack, string needle) {
        if (needle.empty()) return 0;
        
        int n = haystack.size(), m = needle.size();
        
        // 计算next数组
        vector<int> next(m, 0);
        for (int i = 1, j = 0; i < m; i++) {
            while (j > 0 && needle[i] != needle[j]) {
                j = next[j - 1];  // 关键:循环回退
            }
            if (needle[i] == needle[j]) {
                j++;
            }
            next[i] = j;
        }
        
        // 匹配过程
        for (int i = 0, j = 0; i < n; i++) {
            while (j > 0 && haystack[i] != needle[j]) {
                j = next[j - 1];  // 失配时跳转
            }
            if (haystack[i] == needle[j]) {
                j++;
            }
            if (j == m) {
                return i - m + 1;  // 找到匹配
            }
        }
        
        return -1;
    }
};

1.3 KMP算法(有哨兵版)

复制代码
class Solution {
public:
    int strStr(string haystack, string needle) {
        if (needle.empty()) return 0;
        
        int n = haystack.size(), m = needle.size();
        
        // 添加哨兵
        string s = " " + haystack;
        string p = " " + needle;
        
        vector<int> next(m + 1, 0);
        
        // 计算next数组
        for (int i = 2, j = 0; i <= m; i++) {
            while (j > 0 && p[i] != p[j + 1]) {
                j = next[j];
            }
            if (p[i] == p[j + 1]) {
                j++;
            }
            next[i] = j;
        }
        
        // 匹配过程
        for (int i = 1, j = 0; i <= n; i++) {
            while (j > 0 && s[i] != p[j + 1]) {
                j = next[j];
            }
            if (s[i] == p[j + 1]) {
                j++;
            }
            if (j == m) {
                return i - m;
            }
        }
        
        return -1;
    }
};

KMP核心思想总结

算法流程:

  1. 预处理:计算模式串的next数组
  2. 匹配:利用next数组在失配时智能跳转

关键理解:

  • next[i]:前i个字符的最长公共前后缀长度
  • 失配时j = next[j-1](回退到已匹配部分的前缀之后)
  • 主串指针不回溯,保证O(n)时间复杂度

151、反转字符串中的单词

2.1 双指针法(最优解)

复制代码
class Solution {
public:
    string reverseWords(string s) {
        string ans;
        int n = s.size();
        
        for (int i = n - 1; i >= 0;) {
            // 跳过尾部空格
            while (i >= 0 && s[i] == ' ') {
                i--;
            }
            if (i < 0) break;  // 处理全空格情况
            
            int end = i;  // 单词结尾位置
            
            // 找到单词开头
            while (i >= 0 && s[i] != ' ') {
                i--;
            }
            
            // 添加空格(非第一个单词)
            if (!ans.empty()) {
                ans += " ";
            }
            
            // 提取单词 [i+1, end]
            ans += s.substr(i + 1, end - i);
        }
        
        return ans;
    }
};

特点

  • 时间复杂度:O(n)
  • 空间复杂度:O(1)(不包括结果字符串)
  • 原地操作,效率最高

2.2 栈方法

复制代码
class Solution {
public:
    string reverseWords(string s) {
        stack<string> stk;
        int n = s.size();
        int i = 0;

        while (i < n) {
            // 跳过前导空格
            while (i < n && s[i] == ' ') i++;
            
            if (i >= n) break;

            // 找到单词结束位置
            int start = i;
            while (i < n && s[i] != ' ') i++;

            // 提取单词
            string word = s.substr(start, i - start);
            stk.push(word);
        }

        string ans;
        while (!stk.empty()) {
            ans += stk.top();
            stk.pop();
            if (!stk.empty()) ans += ' ';
        }
        return ans;
    }
};

特点

  • 时间复杂度:O(n)
  • 空间复杂度:O(n)
  • 思路清晰,易于理解

2.3 字符串流方法(最简洁)

复制代码
class Solution {
public:
    string reverseWords(string s) {
        stringstream ss(s);
        string word, ans;
        
        while (ss >> word) {
            if (!ans.empty()) ans = " " + ans;
            ans = word + ans;
        }
        
        return ans;
    }
};

特点

  • 代码最简洁
  • 自动处理空格分割
  • 时间复杂度:O(n),空间复杂度:O(n)

算法对比总结

KMP算法对比:

方法 时间复杂度 空间复杂度 特点
暴力法 O(n×m) O(1) 简单直观,效率低
KMP无哨兵 O(n+m) O(m) 推荐,易于理解
KMP有哨兵 O(n+m) O(m) 代码简洁,边界处理简单

反转单词对比:

方法 时间复杂度 空间复杂度 特点
双指针法 O(n) O(1) 最优解,原地操作
栈方法 O(n) O(n) 思路清晰,易于理解
字符串流 O(n) O(n) 代码最简洁

核心技巧总结

KMP关键点:

  1. next数组计算while循环确保完全回退
  2. 失配跳转j = next[j-1] 利用已匹配信息
  3. 主串指针不回溯:保证线性时间复杂度

反转单词关键点:

  1. 双指针技巧:一个找单词头,一个找单词尾
  2. 边界处理:正确处理连续空格和边界情况
  3. 字符串拼接 :使用 if (!ans.empty()) 避免开头多余空格
相关推荐
2301_764441333 分钟前
使用python构建的应急物资代储博弈模型
开发语言·python·算法
小熳芋6 分钟前
验证二叉搜索树- python-递归&上下界约束
数据结构
hetao173383729 分钟前
2025-12-11 hetao1733837的刷题笔记
c++·笔记·算法
Xの哲學34 分钟前
Linux电源管理深度剖析
linux·服务器·算法·架构·边缘计算
小飞Coding38 分钟前
一文讲透 TF-IDF:如何用一个向量“代表”一篇文章?
算法
算家计算1 小时前
突然发布!GPT-5.2深夜来袭,3个版本碾压人类专家,打工人该怎么选?
算法·openai·ai编程
s09071362 小时前
Xilinx FPGA 中ADC 数据下变频+ CIC 滤波
算法·fpga开发·fpga·zynq
TL滕3 小时前
从0开始学算法——第十二天(KMP算法练习)
笔记·学习·算法
Math_teacher_fan3 小时前
第二篇:核心几何工具类详解
人工智能·算法
汉克老师3 小时前
CCF-NOI2025第二试题目与解析(第二题、集合(set))
c++·算法·noi·子集卷积·sos dp·mod 异常