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()) 避免开头多余空格
相关推荐
weixin_499771559 小时前
C++中的组合模式
开发语言·c++·算法
iAkuya9 小时前
(leetcode)力扣100 62N皇后问题 (普通回溯(使用set存储),位运算回溯)
算法·leetcode·职场和发展
近津薪荼9 小时前
dfs专题5——(二叉搜索树中第 K 小的元素)
c++·学习·算法·深度优先
xiaoye-duck9 小时前
吃透 C++ STL list:从基础使用到特性对比,解锁链表容器高效用法
c++·算法·stl
松☆9 小时前
CANN与大模型推理:在边缘端高效运行7B参数语言模型的实践指南
人工智能·算法·语言模型
java干货10 小时前
为什么 “File 10“ 排在 “File 2“ 前面?解决文件名排序的终极算法:自然排序
开发语言·python·算法
皮皮哎哟10 小时前
数据结构:嵌入式常用排序与查找算法精讲
数据结构·算法·排序算法·二分查找·快速排序
程序员清洒10 小时前
CANN模型剪枝:从敏感度感知到硬件稀疏加速的全链路压缩实战
算法·机器学习·剪枝
vortex510 小时前
几种 dump hash 方式对比分析
算法·哈希算法
堕27410 小时前
java数据结构当中的《排序》(一 )
java·数据结构·排序算法