dcddc

西米大人的博客

0%

字符串查找

题目描述

对于一个给定的 source 字符串和一个 target 字符串,你应该在 source 字符串中找出 target 字符串出现的第一个位置(从0开始)。如果不存在,则返回 -1。

思路

  • KMP算法的典型应用

  • KMP的核心思想是要构造next数组。

  • next[i]的含义
    target的第i个字符及其之前的字符组成的子字符串与从target头部开始匹配的字符个数

  • next数组的求法
    初始化:next[0]=next[1]=0

  • next数组在字符串查找中,扮演了极其重要的角色。

    • 当target[i]和source[j]不匹配时,不需要完全回退到target[0]重新开始比较,通过查找next数组,可以知道source[j-1]处已经匹配了多少个target字符,所以source[j]可以从其后的target字符开始比较,无需回退source,大大减少了重复比较的次数。
  • 不用KMP的暴力比较,其时间复杂度是
    O(source.length() * target.length())

  • KMP的时间复杂度是
    O(source.length() + target.length())
    此图帮助你理解KMP的思想:

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
private int[] next;
public int strStr(String source, String target) {
// 特殊情况处理
if(source == null || target == null) {
return -1;
}
if(target.equals("")) {
return 0;
}


// 预处理target字符串,得到next数组
kmpPreview(target);

int sourceIndex = 0;
int targetIndex = 0;
while(sourceIndex != source.length()) { // 如果访问完了source还未找到匹配,则不包含target
//对应字符相等的处理
if(source.charAt(sourceIndex) == target.charAt(targetIndex)) {
// target匹配成功
if(targetIndex == target.length()-1) {
return sourceIndex - target.length() + 1;
} else {
sourceIndex++;
targetIndex++;
}
} else { // 对应字符不等的处理
// 如果回退到target头部仍不相等,则放弃当前的进度,开始比较source的下一个字符
if (targetIndex == 0) {
sourceIndex++;
} else {
// 对应字符不等的处理,由next数组回退target下标,依然和当前的source对应的字符比较,利用了之前比较的进度
targetIndex = next[targetIndex];
}
}
}
// 不包含target
return -1;
}

/**
* @intention:构造next数组。next[i]表示s的第i个字符及其之前的字符组成的子字符串,与从s头开始完全匹配的字符个数
*/
private void kmpPreview(String s) {
next = new int[s.length()+1];
next[0] = 0;
next[1] = 0;
for(int i = 2; i <= s.length(); i++) {
if(s.charAt(i-1) == s.charAt(next[i-1])) {
next[i] = next[i-1] + 1;
} else {
next[i] = 0;
}
}
}

考差点

  • KMP算法