找出字符串中第一个匹配项的下标&重复的子字符串
一、 找出字符串中第一个匹配项的下标
1.方法概述
- 主要就四个步骤初始化next数组,前后缀不相等的情况,前后缀相等的情况,更新next数组。分别定义一个i(主串),j(子串)下标来表示遍历主串和子串的时开始位置,两个下标都从0下标开始。判断当主串和子串长度为0或者主串子串为null直接返回-1。创建一个大小等于子串的数组,使用getNext函数拿到next数组。当i<lenStr(主串长度)和j<lenSub(子串长度)时循环遍历(while),当找到相同字符时,i++,j++。不相同时,i不回退,j回退到 j = next[j]。接下来如果j>=lenSub长度则返回i-j表示全部匹配完成。否则返回-1。
2.具体实现
Java实现
点击查看代码
class Solution {
public void getNext(int[] next,String s){
next[0] = -1;
int i = 2;
int j = 0;
while(i<s.length()){
if((j == -1) || s.charAt(j) == s.charAt(i-1)){
next[i] = j+1;
i++;
j++;
}else{
j = next[j];
}
}
}
public int strStr(String haystack, String needle) {
if(haystack == null || needle == null){
return -1;
}
if(needle.length() == 0 || haystack.length() == 0){
return -1;
}
int i = 0;
int j = 0;
int[] next = new int[needle.length()];
getNext(next,needle);
while(i < haystack.length() && j < needle.length()){
if((j==-1)||(haystack.charAt(i) == needle.charAt(j))){
i++;
j++;
}else{
j = next[j];
}
}
if(j >= needle.length()){
return i-j;
}else{
return -1;
}
}
}
3.要点总结
(1)几个概念的说明
什么是KMP,KMP是用来处理什么问题的?
- KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人们称它为克努特—莫里斯—普拉特操作(简称KMP算法)。KMP算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是通过一个next()函数实现,函数本身包含了模式串的局部匹配信息。
前缀,后缀是什么?
- 前缀:是指不包含最后一个字符的所有以第一个字符开头的连续子串。
- 后缀:是指不包含第一个字符的所有以最后一个字符结尾的连续子串。
前缀表是什么?
- 是用来记录下标j(子串)之前(包括j)的字符串中,有多大长度的相同前缀后缀。


什么是next数组?
- 一般是前缀表右移一位,初始位置为-1后作为next数组。

(2)next数组处理的问题,以及如何初始化
- KMP算法的关键在next[]数组,也就是用 next[j] = k来表示,不同的 j 来对应一个 K 值, 这个 K 就是你将来要移动的 j要移动的位置。
K 的值是这样求的:
- 规则:找到匹配成功部分的两个相等的真子串(不包含本身),一个以下标 0 字符开始,另一个以 j-1 下标字符结尾。
- 不管什么数据 next[0] = -1;next[1] = 0;在这里,我们以下标来开始,而说到的第几个第几个是从 1 开始;也就数组整体右移一位,首位赋初值为-1.
- 假设当 next[j] = k 时有 p[0]~p[k-1] = p[x]~p[j-1] 即有 k-1-0 = j-1-x,x = j-k ,即p[0]~p[k-1] = p[j-k]~p[j-1]。如果p[j] == p[k],则有next[j+1] = k+1,如果p[j] != p[k]则next[j+1] = ?等于多少呢,就需要一直回退直到找到p[j] == p[k],然后有又有next[i+1] = k+1。
二、重复的子字符串
1.方法概述
-需要在预处理next[]数组后,判断n是否为n-next[n-1]-1的倍数。
2.具体实现
Java实现
点击查看代码
class Solution {
public boolean repeatedSubstringPattern(String s) {
if (s.equals("")) return false;
int len = s.length();
s = " " + s;
char[] chars = s.toCharArray();
int[] next = new int[len + 1];
for (int i = 2, j = 0; i <= len; i++) {
while (j > 0 && chars[i] != chars[j + 1]) j = next[j];
if (chars[i] == chars[j + 1]) j++;
next[i] = j;
}
if (next[len] > 0 && len % (len - next[len]) == 0) {
return true;
}
return false;
}
}
3.要点总结
(1)为什么使用KMP来解决该题?
- 在由重复子串组成的字符串中,最长相等前后缀不包含的子串就是最小重复子串,就是最小重复单位。我们可以利用KMP来找到该最小重复单位。

(2)如何确定最小重复子串?
- 因为字符串s的最长相同前后缀的长度一定是不包含字符串本身,最所以最长相等前后缀的长度为next[len - 1] +1,如果len%(len-(next[len-1]+1)) == 0,则说明数组的长度正好可以被 (数组长度-最长相等前后缀的长度) 整除 ,说明该字符串有重复的子字符串。

找出字符串中第一个匹配项的下标&重复的子字符串
浙公网安备 33010602011771号