题目地址:
https://leetcode.com/problems/implement-strstr/
给定两个字符串,判断第二个是否是第一个的子串,如果是则返回匹配的起始地址,否则返回。
法1:暴力法。直接枚举所有的开始位置,依次做匹配,一旦匹配成功则返回起始点下标,如果一直未成功则返回。代码如下:
public class Solution {
public int strStr(String s, String p) {
if (s == null || p == null) {
return -1;
}
if (p.isEmpty()) {
return 0;
}
// i后面要留足p的长度这么多位置
for (int i = 0; i + p.length() - 1 < s.length(); i++) {
// 开始从p[0]开始匹配
int idx = 0;
while (idx < p.length()) {
if (s.charAt(i + idx) != p.charAt(idx)) {
break;
}
idx++;
}
// 匹配完了说明找到了子串,返回起始点下标
if (idx == p.length()) {
return i;
}
}
return -1;
}
}
时间复杂度,空间。
法2:KMP。KMP算法有一个next数组的概念,首先参考https://blog.csdn.net/qq_46105170/article/details/106168535,在未改进版本的KMP中,的next数组表示中最长的相等前后缀的长度,而在改进版本中,则表示当与不匹配的时候,里下一个要与匹配的字符的下标。未改进版本的KMP中,这个下标取的是最长的相等前后缀的长度,利用这个信息去移动,可以在不错过可能解的情况下,保证移动后和比较时,之前的字符已经全部相等,也就是不用再进行匹配了;但是它还可以利用一个信息,若移动后与对齐的那个字符仍然与相等,那么肯定会出错,还是得继续向后移,改进的KMP算法就是将这个信息加入进去。首先,若,那么下一个与对齐的字符变为了,若还不等,则对齐的是,直到相等或者变为为止。如果我们已经知道了,那再次对齐就没有意义,因为还要取一次next。所以索性在一开始就”next到底“,杜绝的情况发生。代码如下:
public class Solution {
public int strStr(String s, String p) {
if (s == null || p == null) {
return -1;
}
if (p.isEmpty()) {
return 0;
}
int i = 0, j = 0;
int[] next = buildNext(p);
while (i < s.length() && j < p.length()) {
if (j == -1 || s.charAt(i) == p.charAt(j)) {
i++;
j++;
} else {
j = next[j];
}
}
return j == p.length() ? i - j : -1;
}
private int[] buildNext(String p) {
int[] next = new int[p.length()];
int i = 0, j = next[0] = -1;
while (i < p.length() - 1) {
if (j == -1 || p.charAt(i) == p.charAt(j)) {
i++;
j++;
// 在未改进的KMP算法中,next[i]直接取j;
// 在改进的算法中需要判断一下当前字符是否和p[j]相等,如果相等则还需要next一下
next[i] = p.charAt(i) == p.charAt(j) ? next[j] : j;
} else {
j = next[j];
}
}
return next;
}
}
时间复杂度,空间。