这是我参与更文挑战的第25天,活动详情查看: 更文挑战
前言
力扣第二十八题 实现 strStr() 如下所示:
实现 strStr() 函数。
给你两个字符串 haystack 和 needle ,请你在 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
一、思路
str:原字符串
target:目标字符串
这一题是手动实现Java中String的Api indexOf,就是找到字符串 needle 在字符串 str 中的位置。如果 target 为空,则返回 0 即可。
手动实现,手动实现,手动实现!重要的事情说三遍,不能使用Java自带的Api哦。
这一题有两种方式来解决:
- 双层循环暴力匹配(方案一)
- KMP算法(俗称看毛片算法)
双层循环此处不做过多的解释,大致的意思就是第一层循环遍历 str 选择起始元素,第二层循环选择 str 中长度为 target.length 的字符串判断是否与 target 相等,如相等返回相应结果即可。
图解KMP算法
此处以 str = ABCABCABF 、 target = ABCABF
tips:说实话我现在还是不知道部分匹配表如何构建,等后续弄明白了再来填坑8!
KMP我觉得 阮一峰,这一篇文章写的挺好的,推荐。
KMP算法的核心思想就是:构建一张部分匹配表,碰到不匹配的元素时可以移动到下一个位置。(具体的步骤见下文)
第一步,构建部分匹配表
ABCABF 的部分匹配表如下所示:
部分匹配表
next中的值表示:当前位置及以前的元素中的前缀和后缀最长的共有元素长度
例如next[4](4为下标,表示第五个元素),表示ABCAB中的前缀和后缀最长的共有元素长度,ABCAB的前缀有A, AB, ABC, ABCA,后缀有BCAB, CAB, AB, B,显然最长的共有元素为AB,即next[4] = 2
移动的公式: 移动位数 = 已匹配的字符数 - 对应的部分匹配值(就是将整个字符移动到下一个可以匹配的位置)
第一次出现字符不相等
第一次移动,此时 F != A,已匹配的字符为 ABCAB 长度为 5,ABCAB 的部分匹配值为 2,故移动的位置为 5 - 2 = 3,如下图所示:
第二次移动,此时 C != A,已匹配的字符为 AB 长度为 2,AB 的部分匹配值为 0,故移动的位置为 2 - 0 = 2,如下图所示:
逐位比较,发现都相同,返回结果下标 5 即可。如下图所示:
二、实现
穷举法
实现代码
public int strStr(String haystack, String needle) {
int n = haystack.length(), m = needle.length();
for (int i = 0; i + m <= n; i++) {
boolean flag = true;
for (int j = 0; j < m; j++) {
if (haystack.charAt(i + j) != needle.charAt(j)) {
flag = false;
break;
}
}
if (flag) {
return i;
}
}
return -1;
}
测试代码
public static void main(String[] args) {
int[] nums = new int[8];
new Number28().strStr("abababca", "abca");
}
结果
KMP算法
实现代码
此处分两部,先求部分匹配表。再使用部分匹配表来求字符串的位置
/**
* KMP算法(俗称:看毛片)
*/
public int strStr(String str, String target) {
int tLen = target.length();
if (tLen == 0) {
return 0;
}
// 部分匹配表
int[] next = getNext(target.toCharArray());
for (int i = 0, j = 0; i < str.length(); i++) {
while (j > 0 && str.charAt(i) != target.charAt(j)) {
j = next[j - 1];
}
if (str.charAt(i) == target.charAt(j)) {
j++;
}
if (j == tLen) {
return i - tLen + 1;
}
}
return -1;
}
/**
* 部分匹配表(Partial Match Table)生成
* 思路:从模式字符串的第一位(注意,不包括第0位)开始对自身进行匹配运算
*/
public int[] getNext(char[] needle) {
int len = needle.length;
int[] next = new int[len];
int j=0;
// next的第一个值一定为0,所以从1开始
for (int i = 1; i < len; i++) {
while (j > 0 && needle[i] != needle[j]) {
j = next[j - 1];
}
if (needle[i] == needle[j]) {
j++;
}
next[i] = j;
}
return next;
}
结果
三、总结
哭惹,怎么看毛片没有穷举快呀tnt!是我写的有什么问题吗?
感谢看到最后,非常荣幸能够帮助到你~♥