Leetcode的AC指南 —— 字符串/KMP:28.找出字符串中第一个匹配项的下标

摘要:
Leetcode的AC指南 —— 字符串/KMP:28.找出字符串中第一个匹配项的下标。题目介绍:给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标(下标从 0 开始)。如果 needle 不是 haystack 的一部分,则返回 -1 。

文章目录

  • 一、题目
  • 二、解析(go)
    • 1、一个简单的AC方法
    • 2、KMP算法:直接使用前缀表作为next数组
  • 三、其他语言版本
    • Java
    • C++
    • Python

一、题目


题目介绍:给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标(下标从 0 开始)。如果 needle 不是 haystack 的一部分,则返回 -1 。

力扣题目链接

示例 1:

输入:haystack = "sadbutsad", needle = "sad"
输出:0
解释:"sad" 在下标 06 处匹配。
第一个匹配项的下标是 0 ,所以返回 0

示例 2:

输入:haystack = "leetcode", needle = "leeto"
输出:-1
解释:"leeto" 没有在 "leetcode" 中出现,所以返回 -1

提示:
1 <= haystack.length, needle.length <= 104
haystack 和 needle 仅由小写英文字符组成

二、解析(go)


1、一个简单的AC方法

思路:让needle在haystack中滑动

go:

func strStr(haystack string, needle string) int {

	runes := []byte(haystack)

	for i := 0; i <= len(haystack)-len(needle); i++ {
		if string(runes[i:len(needle)+i]) == needle {
			return i
		}
	}

	return -1

}

  • 时间复杂度O(n-m)
  • 空间复杂度O(n)

2、KMP算法:直接使用前缀表作为next数组

思路:

  • 构造next数组

    1. 初始化:前缀末尾指针before指向第一个字符,第一个子串的前后缀长度next[0]为0
    2. 默认before指针之前的字符与behind指针之前的字符相等。
    3. 比较before指向的字符与behind指向的字符是否相等。
      • 当不相同时,before指向前一个字符,重新比较,直到before指向第一个字符或者before与behind的字符相等时,结束比较。
      • 当又相同时,before指向下一个字符。
    4. before的位置就是最长前后缀长度,更新next数组。
  • 使用next数组做匹配

    • 初始化
      • i为文本串起始位置,j为模式串起始位置
      • 得到next数组
    • 在文本串中查找模式串,对比i和j分别对应的字符
      • 若i和j相等,则i和j同时后移
      • 若i和j不相等,则j寻找之前匹配的位置,
        • 也就是通过next数组的最大相同子串长度滑动j,确定从模式串的哪个位置开始重新比较
      • 当 j 比完模式串的最后一个字符后,说明模式串在文本串中出现过,返回结果
      • 若文本串遍历结束,j也没有指向模式串的最后一个字符,就返回-1
//直接将前缀表作为next数组
func getnext(next []int, s string) {
    // before: 前缀末尾,前缀长度
    // behand:后缀末尾
    
    before := 0
    next[0] = 0
    for behind := 1; behind < len(s); behind++ {
        for before > 0 && s[behind] != s[before] { // 前缀与后缀不相等
            before = next[befor-1]
        }
        if s[behind] == s[before] { // 前缀与后缀相等
            before++
        }
        next[behind] = before
    }
}
// 直接将前缀表作为next数组的匹配实现
func strStr(haystack string, needle string) int {
    j := 0 // j 为模式串的起始位置, next数组记录的起始位置为0
    next := make([]int, len(needle))
    getnext(next, needle)
    for i := 0; i < len(haystack); i++ { // i从0开始
       for j > 0 && haystack[i] != needle[j] { // 不匹配
          j = next[j-1] // j寻找之前匹配的位置
       }
       if haystack[i] == needle[j] { // 匹配,j 和 i同时向后移动
          j++ // i的增加在for循环中
       }
       if j == len(needle) { // 文本串haystack中出现了模式串needle
          return i - j + 1
       }
    }
    return -1
}

  • n为文本串长度,m为模式串长度。
  • 匹配的过程是O(n),
  • 单独生成next数组,时间复杂度是O(m)。
  • 整个KMP算法的时间复杂度是O(n+m)的。

三、其他语言版本


Java

class Solution {
    //前缀表(不减一)Java实现
    public int strStr(String haystack, String needle) {
        if (needle.length() == 0) return 0;
        int[] next = new int[needle.length()];
        getNext(next, needle);

        int j = 0;
        for (int i = 0; i < haystack.length(); i++) {
            while (j > 0 && needle.charAt(j) != haystack.charAt(i)) 
                j = next[j - 1];
            if (needle.charAt(j) == haystack.charAt(i)) 
                j++;
            if (j == needle.length()) 
                return i - needle.length() + 1;
        }
        return -1;

    }
    
    private void getNext(int[] next, String s) {
        int j = 0;
        next[0] = 0;
        for (int i = 1; i < s.length(); i++) {
            while (j > 0 && s.charAt(j) != s.charAt(i)) 
                j = next[j - 1];
            if (s.charAt(j) == s.charAt(i)) 
                j++;
            next[i] = j; 
        }
    }
}

C++

class Solution {
public:
    void getNext(int* next, const string& s) {
        int j = 0;
        next[0] = 0;
        for(int i = 1; i < s.size(); i++) {
            while (j > 0 && s[i] != s[j]) {
                j = next[j - 1];
            }
            if (s[i] == s[j]) {
                j++;
            }
            next[i] = j;
        }
    }
    int strStr(string haystack, string needle) {
        if (needle.size() == 0) {
            return 0;
        }
        int next[needle.size()];
        getNext(next, needle);
        int j = 0;
        for (int i = 0; i < haystack.size(); i++) {
            while(j > 0 && haystack[i] != needle[j]) {
                j = next[j - 1];
            }
            if (haystack[i] == needle[j]) {
                j++;
            }
            if (j == needle.size() ) {
                return (i - needle.size() + 1);
            }
        }
        return -1;
    }
};

Python


class Solution:
    def getNext(self, next: List[int], s: str) -> None:
        j = 0
        next[0] = 0
        for i in range(1, len(s)):
            while j > 0 and s[i] != s[j]:
                j = next[j - 1]
            if s[i] == s[j]:
                j += 1
            next[i] = j
    
    def strStr(self, haystack: str, needle: str) -> int:
        if len(needle) == 0:
            return 0
        next = [0] * len(needle)
        self.getNext(next, needle)
        j = 0
        for i in range(len(haystack)):
            while j > 0 and haystack[i] != needle[j]:
                j = next[j - 1]
            if haystack[i] == needle[j]:
                j += 1
            if j == len(needle):
                return i - len(needle) + 1
        return -1

你可能感兴趣的:(leetcode的AC指南,leetcode,算法)