LeetCode每日刷题No.28 Implement strStr()


原题

实现 strStr() 函数。

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

示例 1:

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

示例 2:

输入: haystack = "aaaaa", needle = "bba"
输出: -1

说明:
当 needle 是空字符串时,我们应当返回什么值呢?这是一个在面试中很好的问题。
对于本题而言,当 needle 是空字符串时我们应当返回 0 。这与C语言的 strstr() 以及 Java的 indexOf() 定义相符。

来源:力扣(LeetCode)
LeetCode No.28 Implement strStr()


前言

有了前两道题的经历,我第一想法就是拿两个指针走的暴力解法,答案的更优解法(Rabin Karp),我看了好一会儿才知道是怎么实现的,第二种解法确实优化了好些地方。

我的解法

话不多说,先献上代码:

class Solution {
    public int strStr(String haystack, String needle) {
        if (needle.length() == 0) return 0;
        char[] a = haystack.toCharArray();
        char[] b = needle.toCharArray();
        int j = 0;
        for (int i = 0; i < haystack.length(); i++) {
            if (a[i] == b[0]) {
                while (j < needle.length() && j + i < haystack.length()) {
                    if (a[i + j] == b[j]){
                        j++;
                    } else {
                        j = 0;
                        break;
                    }
                }
                if (j == needle.length()) return i;
            }
        }
        return -1;
    }
}

在haystack中从头开始找有没有和needle的头字符相同的字符,如果有,则开始迭代判断,判断下一个是否为相同字符,直到遇到不相同的字符时退出这一轮迭代,或者这一轮迭代的字符与needle字符串全部相同,则返回答案(开始迭代的haystack字符下标)。
执行结果如下:
执行结果1
真叫一个惨目忍睹啊。。。
写完之后翻阅了一下答案的写法

答案解法1

public class Solution {

    public int strStr(String haystack, String needle) {
        int L = needle.length();
        int n = haystack.length();
        for (int start = 0; start < n - L + 1; start++) {
            if (haystack.substring(start, start + L).equals(needle)) {
                return start;
            }
        }
        return -1;
    }
}

作者:LeetCode
来源:力扣(LeetCode)

思路大同小异,但是我发现,原来我java基础不牢…都不知道有这个函数可以使用的。
substring(start, start + L):字符串haystack的子串(下标为start到start + L),然后equals()方法比较字符串是否相等。

执行结果还让我挺意外的:
执行结果2

答案解法2

这个答案的方法,我就特别难想到了,因为大二上学期才转到计算机专业的,数据结构自学学了个寂寞…
此方法就避免了每一次判断,都要从子串的最开头部分开始遍历判断每一个字符,显而易见,这样效率会被拉低,因此,有了Rabin Karp(常数复杂度)解法。

代码如下:

class Solution {
  // function to convert character to integer
  public int charToInt(int idx, String s) {
    return (int)s.charAt(idx) - (int)'a';
  }

  public int strStr(String haystack, String needle) {
    int L = needle.length(), n = haystack.length();
    if (L > n) return -1;

    // base value for the rolling hash function
    int a = 26;
    // modulus value for the rolling hash function to avoid overflow
    long modulus = (long)Math.pow(2, 31);

    // compute the hash of strings haystack[:L], needle[:L]
    long h = 0, ref_h = 0;
    for (int i = 0; i < L; ++i) {
      h = (h * a + charToInt(i, haystack)) % modulus;
      ref_h = (ref_h * a + charToInt(i, needle)) % modulus;
    }
    if (h == ref_h) return 0;

    // const value to be used often : a**L % modulus
    long aL = 1;
    for (int i = 1; i <= L; ++i) aL = (aL * a) % modulus;

    for (int start = 1; start < n - L + 1; ++start) {
      // compute rolling hash in O(1) time
      h = (h * a - charToInt(start - 1, haystack) * aL
              + charToInt(start + L - 1, haystack)) % modulus;
      if (h == ref_h) return start;
    }
    return -1;
  }
}
作者:LeetCode
来源:力扣(LeetCode)

有一说一,要我直接看代码我是看不懂的,不过官方的解析还是挺明确的

我对其理解就是,把子串全部生成为哈希码,打个比方,如要在字符串abcde中找到bcde,操作为:首先,abcde的子串abcd用整型数字1234表示,若要滑动字符串变成bcde,则操作为:(1234 - 1000)* 10 + 5, 则变成了2345,最后直接判断这个哈希码与目标字符串的哈希码是否相同就可以了。


总结

第一种方法很容易想到而且可以很快就写出来,第二种(哈希)写法特别高级,很值得去学习,但是我们要注意到它的很多细节部分,比如我们该如何设计一个公式来存储目标,如何避免溢出(比如哈希值太大,大于整型的最大值)(一般情况下用取模解决),避免了溢出问题,又该考虑怎样能避免散列冲突,之后我专门总结一下散列的相关知识点,来搞清这些问题。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值