題目地址:
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;
}
}
時空複雜度一樣。