【每天学习一点算法 2025/12/03】实现 strStr()

每天学习一点算法 2025/12/03

题目:实现 strStr()

给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标(下标从 0 开始)。

如果 needle 不是 haystack 的一部分,则返回 -1 。

  1. 这种问题最简单的方法就是暴力枚举,直接枚举出 haystack 中所有与 needle 相同长度的字符串,看是否有与 needle 相等的字符串即可。

    function strStr(haystack: string, needle: string): number {
      const hayLen = haystack.length;
      const needleLen = needle.length;
    
      // 空needle返回0
      if (needleLen === 0) return 0;
      // needle比haystack长,直接返回-1
      if (needleLen > hayLen) return -1;
    
      for (let i = 0; i <= hayLen - needleLen; i++) {
        let match = true;
        // 逐字符对比,避免切片创建临时字符串
        for (let j = 0; j < needleLen; j++) {
          if (haystack[i + j] !== needle[j]) {
            match = false;
            break; // 不匹配直接跳出内层循环,减少对比次数
          }
        }
        if (match) return i;
      }
    
      return -1;
    }
    
  2. 还有一个方法就是KMP(Knuth-Morris-Pratt)算法,这个算法的核心就是在已经完成部分匹配子串中找到公共的前缀和后缀,下一次匹配从后缀的下一位开始匹配,这里举个栗子吧,好理解一点。

    假设:主串 haystack = "ABABABC",模式串 needle = "ABABC"

    1. 初始:i=0j=0

    2. haystack[0] = A == needle[0] = Ai=1j=1

    3. haystack[1] = B == needle[1] = Bi=2j=2

    4. haystack[2] = A == needle[2] = Ai=3j=3

    5. haystack[3] = B == needle[3] = Bi=4j=4

    6. haystack[4] = A != needle[4] = C 这个时候遇到不匹配字符时,KMP和普通解法的区别就来了

      普通解法:i = 1j = 0,继续匹配

      KMP:因为已匹配的字符串 ABAB 拥有公共的前后缀 AB,我们可以从i = 5j = 3 继续匹配

    7. 现在对比 haystack[4] = A == needle[2] = Ai=5j=3

    8. haystack[5] = B == needle[3] = Bi=6j=4

    9. haystack[6] = C == needle[4] = Ci=7j=5;对比完成

    算法的大概思想是明白了,但是怎么实现呢?其他步骤都还好,关键在于怎么找到公共的前后缀。

    因为是在已匹配成功的字串中寻找公共前后缀,所以我们只需把needle每个子串的公共前后缀情况记录下来就行了,这里为 needle 生成「部分匹配表(LPS 数组)」—— 该数组存储了模式串每个位置的「最长前缀后缀匹配长度」。

    // 生成部分匹配表(Longest Prefix Suffix)
    function getLPS(needle: string): number[] {
      const len = needle.length;
      const lps = new Array(len).fill(0);
      let length = 0; // 最长匹配前缀的长度
    
      for (let i = 1; i < len; ) {
        if (needle[i] === needle[length]) {
          length++;
          lps[i] = length;
          i++;
        } else {
          if (length > 0) {
            length = lps[length - 1]; // 回退长度
          } else {
            lps[i] = 0;
            i++;
          }
        }
      }
      return lps;
    }
    

    然后就是遍历haystack 开始匹配

    function strStr(haystack: string, needle: string): number {
      const hayLen = haystack.length;
      const needleLen = needle.length;
    
      if (needleLen === 0) return 0;
      if (needleLen > hayLen) return -1;
    
      // 预处理needle,生成部分匹配表(最长前缀后缀匹配长度)
      const lps = getLPS(needle);
    
      let i = 0; // haystack指针
      let j = 0; // needle指针
    
      while (i < hayLen) {
        if (haystack[i] === needle[j]) {
          i++;
          j++;
          if (j === needleLen) {
            return i - j; // 匹配成功,返回起始索引
          }
        } else if (j > 0) {
          // 不匹配时,根据lps回退needle指针(避免重复对比)
          j = lps[j - 1];
        } else {
          i++;
        }
      }
    
      return -1;
    }
    
  3. 叽里咕噜那么一长篇说啥呢?跟我的 indexOf 说去吧

    // String.prototype.indexOf()
    function strStr(haystack: string, needle: string): number {
      return haystack.indexOf(needle)
    }
    

题目来源:力扣(LeetCode)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值