1 题目地址
2 题目说明
给你两个字符串
haystack
和needle
,请你在haystack
字符串中找出needle
字符串的第一个匹配项的下标(下标从 0 开始)。如果needle
不是haystack
的一部分,则返回-1
。示例 1:
输入:haystack = "sadbutsad", needle = "sad" 输出:0 解释:"sad" 在下标 0 和 6 处匹配。 第一个匹配项的下标是 0 ,所以返回 0 。示例 2:
输入:haystack = "leetcode", needle = "leeto" 输出:-1 解释:"leeto" 没有在 "leetcode" 中出现,所以返回 -1 。提示:
1 <= haystack.length, needle.length <= 104
haystack
和needle
仅由小写英文字符组成
3 解题思路
方式一:暴力方式:两层循环
1、假设字符串haystack长度是m,needle长度是n
2、haystack匹配needle最多需要循环次数是m-n+1
3、第一层循环是最多需要遍历的次数m-n+1,即haystack的起始的索引下标位置
4、第二层循环每次都是从0开始最大到needle长度n,判断两层的指向的字符是否相等,
不相等:直接跳出第二层循环
相等:第二层是否遍历到needle的末尾
是:直接返回第一层索引下标(结果)
否:上下两层同时向后移动
方式二:KMP
查看 28-算法打卡-字符串-KMP算法理论-第二十八天-CSDN博客、29-算法打卡-字符串-KMP算法理论2-第二十九天-CSDN博客
核心思想是记录已经遍历过的模式串的特征,当匹配失败的时候只需要模式串退回,文本串继续向右移动,减少时间复杂度O(m+n)。
获取前缀表(next数组):
1、初始化:初始化指针i j, j指向前缀的末尾也是子串的最长公共前后缀、i指向后缀的
末尾也是待更新next数组的位置; j=0,next[0]=0
2、前后缀相同: j向右移动,next[i]=j
3、前后缀不相同:j回退到next[j-1]位置 ,不相等一直回退,即是while,而不是if4、更新next数组,next[i]=j
获取匹配下标:
1、获取next数组
2、定义指针i j,i指向
haystack
开始位置(0), j指向needle
开始位置(0)3、文本串和模式串指向的位置相同时:则两者都继续向右移动
4、文本串和模式串指向的位置不相同时:模式串退回到next[j-1]的位置
4 代码编写
4.1 暴力方式
class Solution {
public int strStr(String haystack, String needle) {
char[] haystackArray = haystack.toCharArray();
char[] needleArray = needle.toCharArray();
int haystackLength = haystackArray.length;
int needleLength = needleArray.length;
// 需要匹配的次数
int need = haystackLength - needleLength + 1;
for (int i=0; i<need; i++) {
int left = i;
int needleInit = 0;
while (needleInit<needleLength) {
// 字符相等
if (haystackArray[left] == needleArray[needleInit]) {
// 最后一个元素也匹配直接返回
if (needleInit == needleLength-1) {
return i;
}
// 否则都向后移动
left++;
needleInit++;
} else {
// 不相等,直接跳出本层循环
break;
}
}
}
return -1;
}
}
简化上面代码方式:
class Solution {
public int strStr(String haystack, String needle) {
char[] haystackArray = haystack.toCharArray();
char[] needleArray = needle.toCharArray();
int haystackLength = haystackArray.length;
int needleLength = needleArray.length;
// 需要匹配的次数
int need = haystackLength - needleLength + 1;
for (int i=0; i<need; i++) {
int left = i;
int needleInit = 0;
// 匹配则向后移动
while (needleInit<needleLength && haystackArray[left] == needleArray[needleInit]) {
left++;
needleInit++;
}
// 最后一个元素也匹配直接返回
if (needleInit == needleLength) {
return i;
}
}
return -1;
}
}

4.2 KMP
class Solution {
public int strStr(String haystack, String needle) {
int m = haystack.length();
int n = needle.length();
int[] next = new int[n];
getNext(needle, next);
int j = 0;
for (int i=0; i<m; i++) {
while (j>0 && haystack.charAt(i) != needle.charAt(j)) {
j = next[j-1];
}
if (haystack.charAt(i) == needle.charAt(j)) {
j++;
}
if (j==n) {
return (i-j+1);
}
}
return -1;
}
// 获取前缀表
public void getNext(String needle, int[] next) {
// 初始化,j后缀的末尾,子串的最长公共前后缀
int j = 0;
next[0] = 0;
// i前缀的末尾,数组next更新索引位置
for (int i=1; i<needle.length(); i++) {
while (j>0 && needle.charAt(i)!= needle.charAt(j)) {
// 向左回退
j = next[j-1];
}
// 匹配成功向右移动
if (needle.charAt(i)== needle.charAt(j)) {
j++;
}
// 更新数组
next[i] = j;
}
}
}