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()) 避免开头多余空格
相关推荐
努力学习的小廉3 小时前
我爱学算法之—— 字符串
c++·算法
yuuki2332333 小时前
【数据结构】常见时间复杂度以及空间复杂度
c语言·数据结构·后端·算法
闻缺陷则喜何志丹3 小时前
【分块 差分数组 逆元】3655区间乘法查询后的异或 II|2454
c++·算法·leetcode·分块·差分数组·逆元
葛小白13 小时前
C#进阶12:C#全局路径规划算法_Dijkstra
算法·c#·dijkstra算法
前端小L3 小时前
图论专题(五):图遍历的“终极考验”——深度「克隆图」
数据结构·算法·深度优先·图论·宽度优先
CoovallyAIHub4 小时前
超越像素的视觉:亚像素边缘检测原理、方法与实战
深度学习·算法·计算机视觉
CoovallyAIHub4 小时前
中科大西工大提出RSKT-Seg:精度速度双提升,开放词汇分割不再难
深度学习·算法·计算机视觉
gugugu.4 小时前
算法:位运算类型题目练习与总结
算法
百***97644 小时前
【语义分割】12个主流算法架构介绍、数据集推荐、总结、挑战和未来发展
算法·架构