题目地址:
https://www.lintcode.com/problem/strstr-ii/description
给定两个字符串source和target,寻找target在source中匹配的第一个位置的下标,如果找不到匹配则返回-1。
法1:KMP算法(未优化版)。解释起来非常费事,这里就省略了。代码如下:
public class Solution {
/*
* @param source: A source string
* @param target: A target string
* @return: An integer as index
*/
public int strStr2(String source, String target) {
// write your code here
// 特判一下
if (source == null || target == null) {
return -1;
}
if (target.isEmpty()) {
return 0;
}
// next[i]表示target[0,...,i - 1]中最长的相等真前缀和真后缀的长度,也表示当target[i]失配时,要接着从target的哪一个下标开始匹配。
int[] next = buildNext(target);
int i = 0, j = 0;
while (i < source.length() && j < target.length()) {
if (j < 0 || source.charAt(i) == target.charAt(j)) {
// 匹配,则继续看下一个字符
i++;
j++;
} else {
// 否则,查表,找一下下一个”值得“匹配的字符
j = next[j];
}
}
return j == target.length() ? i - j : -1;
}
private int[] buildNext(String target) {
int[] next = new int[target.length()];
int t = next[0] = -1;
int j = 0;
// 这个循环实际上是在算next[j + 1]
while (j < target.length() - 1) {
if (t < 0 || target.charAt(j) == target.charAt(t)) {
next[++j] = ++t;
} else {
t = next[t];
}
}
return next;
}
}
时间复杂度(buildNext需要,考虑,那么在while循环里,要么和同时增加,要么被替换为从而也会增加。所以整个while循环里,是严格单调增加的。算法结束的时候,最大是,而最小是,所以),空间。为source的长度,为target长度。
法2:KMP算法(优化版)。代码如下:
public class Solution {
/*
* @param source: A source string
* @param target: A target string
* @return: An integer as index
*/
public int strStr2(String source, String target) {
// write your code here
if (source == null || target == null) {
return -1;
}
if (target.isEmpty()) {
return 0;
}
int[] next = buildNext(target);
int i = 0, j = 0;
while (i < source.length() && j < target.length()) {
if (j < 0 || source.charAt(i) == target.charAt(j)) {
i++;
j++;
} else {
j = next[j];
}
}
return j == target.length() ? i - j : -1;
}
private int[] buildNext(String target) {
int[] next = new int[target.length()];
int t = next[0] = -1;
int j = 0;
while (j < target.length() - 1) {
if (t < 0 || target.charAt(j) == target.charAt(t)) {
// 唯一优化的地方在这里。
// 当target[j] = target[t]的时候,我们已经知道target[j]和文本串不匹配了,
// 那再把target[t]拿过来匹配会是无用功,所以直接再跳一步
// 那为什么只需要再跳一步呢,这是因为在算next[t]的时候,
// 我们已经保证了target[t]和target[next[t]]是不等的了,所以不需要跳两步
j++;
t++;
next[j] = target.charAt(j) != target.charAt(t) ? t : next[t];
} else {
t = next[t];
}
}
return next;
}
}
时空复杂度一样。