字符串算法题(6)实现strStr()

目录

实现strStr()

描述

说明

示例 1

示例 2

示例 3

提示

方法:暴力搜索

 方法二:KMP算法


实现strStr()

描述

实现 strStr() 函数。

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

说明

当 needle 是空字符串时,我们应当返回什么值呢?这是一个在面试中很好的问题。

对于本题而言,当 needle 是空字符串时我们应当返回 0 。

这与 C 语言的 strstr() 以及 Java 的 indexOf() 定义相符。

示例 1

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

示例 2

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

示例 3

输入:haystack = "", needle = ""
输出:0

提示

  • 0 \le haystack.length, needle.length \le 5 * 10^4
  • haystack 和 needle 仅由小写英文字符组成

方法:暴力搜索

暴力搜索的方法大家应该都熟:拿匹配串在原串上一个个比较,不匹配则右移一位继续比较。

class Solution {
    public int strStr(String haystack, String needle) {
        int len_haystack = haystack.length(), len_needle = needle.length();
        char[] haystackArr = haystack.toCharArray(), needleArr = needle.toCharArray();
        for (int i = 0; i <= len_haystack - len_needle; i++) {// 从原串的「发起点」和匹配串的「首位」开始,尝试匹配
            int a = i, b = 0;
            while (b < len_needle && haystackArr[a] == needleArr[b]) {//对原串和匹配串进行匹配,直到结束或者某位不同
                a++;
                b++;
            }
            if (b == len_needle) return i;// 如果能够完全匹配,返回原串的「发起点」下标
        }
        return -1;
    }
}

 

 方法二:KMP算法

KMP是D.E.Knuth、J,H,Morris和V.R.Pratt三位共同提出的,即Knuth-Morris-Pratt算法(简称KMP算法),是用于解决字符串匹配问题的一种方法。

我们举个例子:如果我们要在原串"aabaabaac"中查找匹配串"aabaac"的下标,当我们完成第一轮匹配发现最后一个字符不同("aabaabaac"和"aabaac"),暴力法则会将原串右移一位,比较"aabaabaac"和"aabaac",发现第二个字符就不同了,然后继续右移直到结束。

而KMP算法精髓在于根据已有的信息快速跳过一些不可能的解,加速我们搜索的过程。提起KMP算法,一个很重要的概念是前缀表。


前缀表

前缀的定义是包含首字符,但不包含尾字符的所有字符串,比如匹配串aabaac的所有前缀有:

  • a
  • aa
  • aab
  • aaba
  • aabaa

这里我们还要提一下后缀的概念,后缀即包含尾字符,但不包含首字符的所有字符串,aabaac的所有后缀有:

  • c
  • ac
  • aac
  • baac
  • abaac

有了这些概念后,我们接着来看看前缀表存储的是什么,前缀表存储的是最长相等前后缀长度

最长相等前后缀长度就是字符串的前缀和后缀相等并且取最大值。我们举个例子,还是字符串aabaac,我们从第一个字符开始求最长相等前后缀长度

  • a:该字符没有前后缀,所以长度为0
  • aa:该字符前后缀均为a,长度为1
  • aab:该字符前后缀没有相等的,所以长度为0
  • aaba:该字符相等前后缀为a,长度为1
  • aabaa:该字符相等前后缀有a、aa,最长长度为2
  • aabaac:该字符前后缀没有相等的,所以长度为0

 所以我们得到这样一个前缀表。


KMP算法

 有了前缀表后,我们先进行第一轮匹配,在字符c处发现不匹配,此时我们查看前缀表,找到下次匹配的下标。

我们查看字符c前面的字符串aabaa,该子串的最长相等前后缀为2,所以我们将匹配串aabaac中下标为2的字符"b"和当前原串中的字符"b"进行匹配,发现可以匹配就结束。

如果此时仍然不匹配,那么就将当前字符和匹配串的第一个字符比较,重新开始匹配。

class Solution {
    public int strStr(String haystack, String needle) {
        if ("".equals(needle)) return 0;//空字符串直接返回0
        int n = haystack.length(), m = needle.length();// 分别读取原串和匹配串的长度
        haystack = " " + haystack;// 原串和匹配串前面都加空格,使其下标从 1 开始
        needle = " " + needle;
        char[] s = haystack.toCharArray();//将字符串转换为字符数组
        char[] p = needle.toCharArray();
        int[] next = new int[m + 1];// 构建 next 数组,数组长度为匹配串的长度(next 数组是和匹配串相关的)

        //构造前缀表
        for (int i = 2, j = 0; i <= m; i++) {// 构造过程 i = 2,j = 0 开始,i 小于等于匹配串长度 【构造 i 从 2 开始】
            while (j > 0 && p[i] != p[j + 1]) j = next[j];// 匹配不成功的话,j = next(j)
            if (p[i] == p[j + 1]) j++;// 匹配成功的话,先让 j++
            next[i] = j;// 更新 next[i],结束本次循环,i++
        }

        //进行匹配
        for (int i = 1, j = 0; i <= n; i++) {// 匹配过程,i = 1,j = 0 开始,i 小于等于原串长度 【匹配 i 从 1 开始】
            while (j > 0 && s[i] != p[j + 1]) j = next[j];// 匹配不成功 j = next(j)
            if (s[i] == p[j + 1]) j++;// 匹配成功的话,先让 j++,结束本次循环后 i++
            if (j == m) return i - m;// 整一段匹配成功,直接返回下标
        }
        return -1;
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值