目录
基本介绍
-
串是由零个或多个字符组成的有限序列,又叫做字符串。
-
零个字符的串称为 空串
-
空格串:是只包含空格的串。它与空串是有区别的,空格串是有内容有长度的。
-
串中任意个数的连续字符组成的子序列称为该串的子串,相应的,包含子串的串称为主串。
-
子串在主串中的位置就是子串的第一个字符在主串中的序号。
-
串也是一种数据结构,串所针对的是字符集,也就是串中的元素都是字符!
-
子串的定位操作通常称作串的模式匹配,应该算是串中最重要的操作之一。
KMP模式匹配算法
核心:KMP的next数组简单来说,假设有两个字符串,一个是待匹配的字符串strText,一个是要查找的关键字strKey。现在我们要在strText中去查找是否包含strKey,用i来表示strText遍历到了哪个字符,用j来表示strKey匹配到了哪个字符。
如果是暴力的查找方法,当 strText[i] 和 strKey[j] 匹配失败的时候,i 和 j 都要回退,然后从 i-j 的下一个字符开始重新匹配。
而KMP就是保证 i 永远不回退,只回退 j 来使得匹配效率有所提升。它用的方法就是利用strKey在失配的 j 为之前的成功匹配的子串的特征来寻找 j 应该回退的位置。而这个子串的特征就是前后缀的相同程度。 所以next数组其实就是查找strKey中每一位前面的子串的前后缀有多少位匹配,从而决定j失配时应该回退到哪个位置。
前缀是指不包含最后一个字符的所有以第一个字符开头的连续子串。后缀是指不包含第一个字符的所有以最后一个字符结尾的连续子串。
next 数组
public static int[] getNext(String ps)
{
char[] strKey = ps.toCharArray();
int[] next = new int[strKey.length];
// 初始条件
int j = 0;
int k = -1;
next[0] = -1;//next 数组第一位默认为 -1,即让 i 右移
// 根据已知的前j位推测第j+1位
while (j < strKey.length - 1)
{
if (k == -1 || strKey[j] == strKey[k])
{
next[++j] = ++k;
}
else
{
k = next[k];
}
}
return next;
}
优化next
public static int[] getNext(String ps)
{
char[] strKey = ps.toCharArray();
int[] next = new int[strKey.length];
// 初始条件
int j = 0;
int k = -1;
next[0] = -1;
// 根据已知的前j位推测第j+1位
while (j < strKey.length - 1)
{
if (k == -1 || strKey[j] == strKey[k]){
// 如果str[j + 1] == str[k + 1],回退后仍然失配,所以要继续回退
if (strKey[j + 1] == strKey[k + 1])
{
next[++j] = next[++k];
}
else
{
next[++j] = ++k;
}
}
else
{
k = next[k];
}
}
return next;
}
手写 KMP 算法
public static int KMP(String ts, String ps) {
char[] t = ts.toCharArray();
char[] p = ps.toCharArray();
int i = 0; // 主串的位置
int j = 0; // 模式串的位置
int[] next = getNext(ps);
while (i < t.length && j < p.length) {
if (j == -1 || t[i] == p[j]) { // 当j为-1时,要移动的是i,当然j也要归0
i++;
j++;
} else {
// i不需要回溯了
// i = i - j + 1;
j = next[j]; // j回到指定位置
}
}
if (j == p.length) {
return i - j;
} else {
return -1;
}
}