给定一个长字符串src,比如一篇文章;再给定一个搜索的目标target,比如一个词语或者一个短句。要找到src中找到子串target出现的第一个位置,如果没有则返回-1。
https://leetcode.cn/problems/implement-strstr/
class Solution {
public int strStr(String src, String target) {
int m = src.length();
int n = target.length();
if(n > m){return -1;}
int flag;
for(int i = 0; i <= m-n; i++){
flag = 0;
for(int j = 0; j < n; j++){
if(src.charAt(i+j) != target.charAt(j)){
flag = 1;
break;
}
}
if(flag == 0){return i;}
}
return -1;
}
}
从src的每一个字符开始往后逐位比对,匹配失败则从下一位开始从头匹配,匹配成功就返回结果。显然,时间复杂度是。
我们以ABABABC中查找ABABC为例,暴力匹配的第一轮结果是:
(ABAB)ABC
(ABAB)C
这里我们发现已经匹配成功的子串中,有公共前后缀AB。那么实际上,中途的很多匹配过程可以跳过,我们只需要从
AB(AB)ABC
(AB)ABC
状态继续匹配即可。假设我们有两个指针i和j,分别指向src和target要比对的元素的位置。那么暴力解法中,i的位置会经常发生回退,从而造成了时间的浪费。而如果把公共前后缀的信息考虑进来,可以大幅提升算法时间复杂度。
第一步:产出和target长度相同的next数组,next[i]表示target[0, ..., i]的最长公共前后缀(不包括自身)。
第二步:初始化遍历指针i=0和j=0,循环执行第三步。遍历过程中如果j已超出target范围,那么匹配成功,返回对应位置。如果i已超出src范围,说明target不存在,返回-1。
第三步:比较src[i]和target[j]。如果两者相等,那么i++,j++,继续执行第三步。如果两者不相等,那么看next[i]。如果next[i] > 0,j回退到next[i];如果next[i] = 0,i++,j=0。length变成next[i]。
首先next[0] = 0是必然事件。假设next[i-1]=k,那么就意味着,target[0, ..., k-1] == target[i-k, ..., i-1]。如果target[i] = target[k],那么target[0, ..., k] == target[i-k, ..., i]一定是最长公共前后缀,next[i]=k+1。
如果target[i]不等于target[k]怎么办呢?此时我们要去看next[k-1],设为t。它表明了target[0, ..., k-1]的最长公共前后缀是target[0, ..., t-1] == target[k-t, k-1],又因为target[0, ..., k-1] == target[i-k, ..., i-1],那么target[0, ..., t-1] == target[i-t, ..., i-1]。于是我们令k=t(即next[k-1])。继续比较target[i]和target[k],重复上述过程即可。
class Solution {
public int strStr(String src, String target) {
int[] next = buildNext(target);
int m = src.length();
int n = target.length();
int i = 0;
int j = 0;
while(i < m){
if(src.charAt(i) == target.charAt(j)){
i++; j++;
}
else if(j == 0){i++;}
else{j = next[j-1];}
if(j == n){return i - j;}
}
return -1;
}
public int[] buildNext(String target){
int length = target.length();
int[] res = new int[length];
res[0] = 0;
int i = 1;
int now = 0;
while(i < length){
if(target.charAt(i) == target.charAt(now)){
now++;
res[i] = now;
i++;
}
else if(now > 0){
now = res[now-1];
}
else{
res[i] = 0;
i++;
}
}
return res;
}
}