28. 实现 strStr()

题目叙述

实现 strStr() 函数。
给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始)。如果不存在,则返回 -1。
示例 1:

输入: haystack = “hello”, needle = “ll”
输出: 2

分析

使用滑动窗口进行暴力求解。窗口的大小最大是匹配字符串needle的长度。时间复杂度为 O ( m ∗ n ) O(m*n) O(mn)

class Solution {
public:
    int strStr(string haystack, string needle) {
        //本题要注意特别考虑needle为零的情况
        if(needle.size()==0) return 0;
        for(int i = 0;i<haystack.size();i++){
            int p = i;
            int q =  0;
            for(;q<needle.size();q++){
                if(haystack[p]!=needle[q]) break;
                p++;
            }
            if(q==needle.size()) return i;
        }
        return -1;  
    }
};

上面的代码中p表示的是窗口的左边界,q表示的是窗口的右边界。

下面有更简洁的写法:

class Solution {
public:
    int strStr(string haystack, string needle) {
        //本题要注意特别考虑needle为零的情况
        if(needle.size()==0) return 0;
        for(int i = 0;i<haystack.size();i++){
            int q =  0;
            for(;q<needle.size();q++){
                if(haystack[q+i]!=needle[q]) break;
            }
            if(q==needle.size()) return i;
        }
        return -1;  
    }
};

Rabin Karp - 常数复杂度

有一种最坏时间复杂度也为 O(N)O(N) 的算法。思路是这样的,先生成窗口内子串的哈希码,然后再跟 needle 字符串的哈希码做比较。

因此问题主要考虑在常数时间复杂度的下求出所有对比子串的哈希值。

第一个问题:如何确定哈希规则(对一个字符串的哈希规则应当是要避免产生碰撞)哈希规则定义如下:
对于一个字符串: abcd 首先将其数字化为0123,由于题目的字符串中只包含小写的字母,那么可以将26个小写的字符转化为0-25的数字。转化为数字之后,就可以定义哈希函数为:
h 0 = 0 × 2 6 3 + 1 × 2 6 2 + 2 × 2 6 1 + 3 × 2 6 0 h_0 = 0×26 ^3 +1×26^2 +2×26^1+3×26^0 h0=0×263+1×262+2×261+3×260
即让字符串中的字母对应的数字乘以相应的权重,权重的底为26,指数为从右到左的字母序号。
写成通式为:
h 0 = c 0 a L − 1 + c 1 a L − 2 + … + c i a L − 1 − i + … + c L − 1 a 0 h_0=c_0 a^{L-1}+c_1 a^{L-2}+\ldots+c_i a^{L-1-i}+\ldots+c_{L-1} a^{0} h0=c0aL1+c1aL2++ciaL1i++cL1a0
其中L为needle的长度, a为26。

当窗口滑动的时候,即左边去掉一个字母,右边增加一个字母,那么我们可以依据之前的哈希结果来计算新的字符串的哈希结果,即设新的字符串的哈希结果为 h 1 h_1 h1, 那么:
h 1 = ( h 0 a − c 0 a L ) + c L h_1 = (h_0 a - c_0a^L) + cL h1=(h0ac0aL)+cL

此外还要避免溢出: a L a^L aL可能是一个很大的数字,因此需要设置数值上限来避免溢出。设置数值上限可以用取模的方式,即用 h % modulus 来代替原本的哈希值。

class Solution {
public:
    int strStr(string haystack, string needle) {
        int L = needle.size();
        int n = haystack.size();
        if(L>n) return -1;
        int a = 26;
        long modulus = (long)pow(2, 31);
        long h = 0;
        long ref_h = 0;
        for (int i = 0; i < L; i++) {
            h = (h * a + haystack[i]-'a') % modulus;
            ref_h = (ref_h * a + needle[i]-'a') % modulus;
        }
        if (h == ref_h) return 0;
        long aL = 1;
        for (int i = 1; i <= L; i++) aL = (aL * a) % modulus;
        for (int start = 1; start < n - L + 1; start++) {
            h = (h * a - (haystack[start-1]-'a') * aL + haystack[start + L - 1]-'a') % modulus;
            if (h == ref_h) return start;
        }
        return -1;  
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值