28. Implement strStr()

题目描述:

Implement strStr().

Return the index of the first occurrence of needle in haystack, or -1 if needle is not part of haystack.

Example 1:

Input: haystack = "hello", needle = "ll"
Output: 2

Example 1:

Input: haystack = "aaaaa", needle = "bba"
Output: -1

题目翻译

返回字符串中第一次出现给定字符串的位置

示例1:

输入: haystack = "hello", needle = "ll"
输出: 2

解题方案

标签: Array

思路:

  • 暴力解法对主串元素和子串进行一一遍历比对,时间复杂度为$O(mn)$,$m$为主串长度,$n$为子串长度。

  • 使用KMP模式匹配算法,时间复杂度可降为$O(m+n)$,这里主要使用KMP算法。

  • 针对传统的模式匹配算法,当子串与主串某一元素不匹配时,子串需要回溯到开始位置,主串开始下一元素的匹配。这样有些子串和主串已经匹配的信息将会丢失,造成冗余匹配。

  • KMP算法针对子串计算$next$数组,主串指针不会回退。记主串为$T[0\cdots n-1]$,子串为$P[0\cdots m-1]$。假设主串和子串分别在第$i$和$j$个位置失配($T[i]\ne P[j]$),那么有$P[0\cdots j-1]=T[i-j\cdots i-1]$,假设下一步主串应该与子串第$k$个字符继续比较(注意主串$i$指针不会回退),此时有$P[0\cdots k-1]=$ $T[i-k\cdots i-1]$,考虑前面$P[0\cdots j-1]=T[i-j\cdots i-1]$,我们可知对于长度为$k$ 的匹配串有$P[j-k\cdots j-1]=T[i-k\cdots i-1]$ ,那么则有$P[0\cdots k-1]=P[j-k \cdots j-1]$,问题转换为求子串前缀和后缀的最长公共子序列的问题。当主串和子串失配时,仅需将子串向右滑动至第$k$个字符和主串中第$i$个字符对齐即可。

    令$next[j]=k$,表示子串第$j$个字符与主串字符失配时,在子串中需重新和主串中该字符重新进行比较的位置。

    $$next[j]=\left{ \begin{aligned}&-1 &j=0\&max{k|1\le k<j且P[0\cdots k-1]=P[j-k\cdots j-1}\&0&otherwise\end{aligned}\right.$$

  • 考虑"ababc"这一字子串

    • "a":$next[0]=-1$;
    • "ab":$next[1]=0$;
    • "aba":前缀[a]后缀[b],无公共元素,next[2]=0;
    • "abab":前缀为[a, ab],后缀为[ba, a],共有元素的长度为1,$next[3]=1$;
    • "ababc"的前缀为[a, ab, aba],后缀为[bab, ab,a],最长共有元素为"ab",长度为2,$next[4]=2$;

代码:

class Solution(object):
    def strStr(self, haystack, needle):
        """
        :type haystack: str
        :type needle: str
        :rtype: int
        """
        next = self.getnext(self, needle)    # 获取next数组
        i = 0    # 主串指针
        j = 0    # 子串指针
        len1 = len(haystack)    # 主串长度
        len2 = len(needle)    # 子串长度
        while i < len1 and j < len2:
            if haystack[i] == needle[j] or j == -1:     # 相等继续向前比较
                i += 1
                j += 1
            else:   # 不等,j 回溯到 next[j]
                j = next[j]
        if j < len2:    # 遍历完,子串仍有空余,即没有找到匹配的位置
            return -1
        else:    # 匹配完成,匹配位置为当前位置-子串长度
            return i-len2

    def getnext(self, needle):  # 获取子串 next 数组,求最长前缀后缀长度,相当于对子串本身进行一次模式匹配
        i = 0
        j = -1
        next = [-1 for i in range(len(needle))]    # next数组初始化为 -1,next[0] = -1
        while i < len(needle) - 1:
            if j == -1 or needle[i] == needle[j]:   # 如果相等,最大前缀后缀长度+1
                i += 1
                j += 1
                next[i] = j
            else:   # 如果不等,j回溯到next[j]
                j = next[j]
        return next

参考资料

  • 《数据结构 C++版本(第二版)》王红梅、胡明、王涛编著

results matching ""

    No results matching ""