Leetcode每日一题2021.2.25第28题:实现 strStr()

题目描述

在这里插入图片描述

示例

在这里插入图片描述

方法一:字串逐一比较 - 线性时间复杂度 O((N-L)L)

思路:

设 needle 的长度为 L, 对 haystack 沿着字符串逐步移动长度为 L 的滑动窗口,将窗口内的子串与 needle 比较。
在这里插入图片描述

代码:

class Solution:
    def strStr(self, haystack: str, needle: str) -> int:
        L, n = len(needle), len(haystack)
        if L == 0:
            return 0

        for start in range(n-L+1):
            if haystack[start: start+L] == needle:
                return start

        return -1

方法二:双指针 - 线性时间复杂度,但可避免比较所有子串

思路:

首先,只有一个子串的第一个字符跟 needle 字符串第一个字符相同的时候才需要比较。
在这里插入图片描述
其次,可以一个字符一个字符比较,一旦不匹配了就立刻终止。
在这里插入图片描述
如下图所示,比较到最后一位时发现不匹配,这时候开始回溯。需要注意的是,pn指针是移动到 pn - curr_len + 1 的位置,而不是 pn - curr_len 的位置。
在这里插入图片描述
这时候再比较一次,就找到了完整匹配的子串,直接返回子串的开始位置 pn - L 。
在这里插入图片描述

算法:

  • 移动 pn 指针,直到 pn 所指向位置的字符与 needle 字符串第一个字符相等。
  • 通过 pn, pL, curr_len 计算匹配长度。
  • 如果完全匹配(即 curr_len == L),返回匹配子串的起始坐标(即 pn - L)。
  • 如果不完全匹配,回溯。使 pn = pn - curr_len + 1, pL = 0, curr_len = 0。

代码:

class Solution:
    def strStr(self, haystack: str, needle: str) -> int:
        L, n = len(needle), len(haystack)
        if L == 0:
            return 0

        pn = 0
        while pn < n - L + 1:
            # 找needle第一个字符在haystack里出现的位置
            while pn < n - L + 1 and haystack[pn] != needle[0]:
                pn += 1

            # 计算最大匹配子串
            pL = curr_len = 0
            while pL < L and pn < n and haystack[pn] == needle[pL]:
                pn += 1
                pL += 1
                curr_len += 1

            # 如果整个needle都被找到了,返回其起始的位置
            if curr_len == L:
                return pn - L

            # 否则,回溯
            pn = pn - curr_len + 1

        return -1

方法三:Rabin Karp - 常数复杂度 O(N)

思路:

先生成窗口内子串的哈希码,然后再跟 needle 字符串的哈希码作比较。

滚动哈希:常数时间生成哈希码

生成一个长度为 L 的数组的哈希码,需要 O(L)的时间。

  • 如何在常数时间生成滑动窗口数组的哈希码?利用滑动窗口的特性,每次滑动都有一个元素进,一个出。

由于只会出现小写的英文字母,因此可以将字符串转化成值 0 ~ 25 的整数数组。例如:

  • 窗口此时是 abcd ,则对应的整数数组为 [0, 1, 2, 3]
  • 窗口滑动到 bcde , 原数组最左边的 0 被移除,同时最右边新添了 4 。

算法:

  1. 计算子字符串 haystack[0, L] 和 needle 的哈希值。
  2. 从起始位置开始遍历:从第一个字符遍历到第 N-L 个字符。
    - 根据前一个哈希值计算滚动哈希。
    - 如果子字符串哈希值与 needle 字符串哈希值相等,返回滑动窗口起始位置。
  3. 返回 -1,这时候 haystack 字符串中不存在 needle 字符串。

代码:

class Solution:
    def strStr(self, haystack: str, needle: str) -> int:
        L, n = len(needle), len(haystack)
        if L > n:
            return -1

        # base value for the rolling hash function
        a = 26
        # modulus value for the rolling hash function to avoid overflow
        modulus = 2**31

        # lambda-function to convert character to integer
        h_to_int = lambda i : ord(haystack[i]) - ord('a')
        needle_to_int = lambda i : ord(needle[i]) - ord('a')

        # compute the hash of strings haystack[:L], needle[:L]
        h = ref_h = 0
        for i in range(L):
            h = (h * a + h_to_int(i)) % modulus
            ref_h = (ref_h * a + needle_to_int(i)) % modulus
        if h == ref_h:
            return 0

        # const value to be used often : a**L % modulus
        aL = pow(a, L, modulus)
        for start in range(1, n-L+1):
            # compute rolling hash in O(1) time
            h = (h * a - h_to_int(start - 1) * aL + h_to_int(start + L -1)) % modulus
            if h == ref_h:
                return start

        return -1
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值