KMP算法学习
(1) 参考网址:
KMP算法: http://www.cnblogs.com/dolphin0520/archive/2011/08/24/2151846.html
--- 用递推思想和直接法两种方式求解next数组
KMP学习心得: http://www.java3z.com/cwbwebhome/article/article19/res023.html?id=3737
--- 当一个字符串以0为起始下标时,next[i]可以描述为"不为自身的最大首尾重复子串长度"。
KMP字符串模式匹配中模式函数: http://www.java3z.com/cwbwebhome/article/article19/re022.html?id=3731
--- 详细讲解了next数组的求解方法【next数组求解的改进版本】
(2) 经典总结:
一、怎么求串的模式值next[n]
定义:
(1)next[0]= –1 意义:任何串的第一个字符的模式值规定为-1。
(2)next[j]= –1 意义:模式串T中下标为j的字符,如果与首字符相同,且j的前面的1~k个字符与开头的1~k个字符不等或者相等但
T[k]==T[j](1≤k<j)。 如:T=”abCabCad” 则 next[6]=-1,因T[3]=T[6] bCa
(3)next[j]=k 意义:模式串T中下标为j的字符,如果j的前面k个字符与开头的k个字符相等,且T[j] != T[k] (1≤k<j)。
即T[0]T[1]T[2]...T[k-1]== T[j-k]T[j-k+1]T[j-k+2]…T[j-1] 且T[j] != T[k].(1≤k<j);
(4)next[j]=0 意义:除(1)(2)(3)的其他情况。
二、next 函数值究竟是什么含义
设在字符串S中查找模式串T,若S[m]!=T[n],那么,取T[n]的模式函数值next[n],
1. next[n]= -1 表示S[m]和T[0]间接比较过了,不相等,下一次比较 S[m+1] 和T[0]
2. next[n]=0 表示比较过程中产生了不相等,下一次比较 S[m] 和T[0]。
3. next[n]= k >0 但k<n, 表示,S[m]的前k个字符与T中的开始k个字符已经间接比较相等了,下一次比较S[m]和T[k]相等吗?
4. 其他值,不可能。
(3) 代码实现 【参考书籍:《数据结构与算法 Java语言描述》 邓俊辉 著】
原书代码:【略有改动】
package dsa;
/*
* 串模式匹配:KMP算法
*/
public class PM_KMP {
public static void main(String[] args) {
System.out.println(PM("ababacab", "aca"));
}
public static int PM(String T, String P) {// KMP算法
int[] next = BuildNextImproved(P);// 构造next[]表
int i = 0;// 主串指针
int j = 0;// 模式串指针
while (j < P.length() && i < T.length()) {// 自左向右逐个比较字符
ShowProgress(T, P, i - j, j);
ShowNextTable(next, i - j, P.length());
System.out.println();
if (0 > j || T.charAt(i) == P.charAt(j)) {// 若匹配,或P已移出最左侧(提问:这两个条件能否交换次序?)
i++;
j++;// 则转到下一对字符
} else
// 否则
j = next[j];// 模式串右移(注意:主串不用回退)
}// while
return (i - j);
}
protected static int[] BuildNext(String P) {// 建立模式串P的next[]表
int[] next = new int[P.length()];// next[]表
int j = 0;// “主”串指针
int t = next[0] = -1;// “模式”串指针
while (j < P.length() - 1)
if (0 > t || P.charAt(j) == P.charAt(t)) {// 匹配
j++;
t++;
next[j] = t;// 此句可以改进...
} else
// 失配
t = next[t];
for (j = 0; j < P.length(); j++)
System.out.print("\t" + P.charAt(j));
System.out.print("\n");
ShowNextTable(next, 0, P.length());
return (next);
}
protected static int[] BuildNextImproved(String P) {// 建立模式串P的next[]表(改进版本)
int[] next = new int[P.length()];
;// next[]表
int j = 0;// “主”串指针
int t = next[0] = -1;// “模式”串指针
while (j < P.length() - 1)
if (0 > t || P.charAt(j) == P.charAt(t)) {// 匹配
j++;
t++;
next[j] = (P.charAt(j) != P.charAt(t)) ? t : next[t];// 注意此句与未改进之前的区别
} else
// 失配
t = next[t];
for (j = 0; j < P.length(); j++)
System.out.print("\t" + P.charAt(j));
System.out.print("\n");
ShowNextTable(next, 0, P.length());
return (next);
}
protected static void ShowNextTable(int[] N, int offset, int length) {// 显示next[]表,供演示分析
int i;
for (i = 0; i < offset; i++)
System.out.print("\t");
for (i = 0; i < length; i++)
System.out.print("\t" + N[i]);
System.out.print("\n\n");
}
protected static void ShowProgress(// 动态显示匹配进展
String T,// 主串
String P,// 模式串
int i,// 模式串相对于主串的起始位置
int j)// 模式串的当前字符
{
int t;
System.out.println("-------------------------------------------");
for (t = 0; t < T.length(); t++)
System.out.print("\t" + T.charAt(t));
System.out.print("\n");
if (0 <= i + j) {
for (t = 0; t < i + j; t++)
System.out.print("\t");
System.out.print("\t|");
}
System.out.println();
for (t = 0; t < i; t++)
System.out.print("\t");
for (t = 0; t < P.length(); t++)
System.out.print("\t" + P.charAt(t));
System.out.print("\n");
System.out.println();
}
}
精简代码:
package dsa.me;
public class Kmp {
public static void main(String[] args) {
System.out.println(kmp("ababa", "bab"));
System.out.println(kmp("ababa", "bba"));
}
// KMP算法
public static int kmp(String T, String P) {
int next[] = buildeNextImproved(P);
int i = 0;
int j = 0;
while (i < T.length() && j < P.length()) {// 一定要限制它们在范围内,不然会报错
if (j < 0 || T.charAt(i) == P.charAt(j)) {// 匹配,i和j都向右移动,j<0(j=-1)
i++;
j++;
} else {// 不匹配,只要移动j,i不要回溯
j = next[j];
}
}
if (j >= P.length())
return (i - j);
else
return -1;
}
// 求next数组
public static int[] buildeNext(String P) {
int[] next = new int[P.length()];
int j = 0;
int t = -1;// 初始值是-1
next[0] = -1;
while (j < P.length() - 1) {
if (t == -1 || P.charAt(t) == P.charAt(j)) {// 匹配---如果在j这里不存在首尾相同的字符串,那么next[j]就会等于0
t++;
j++;
next[j] = t;// 由这里看出while条件中j不能等于P.length()-1
} else {// 不匹配
t = next[t];
}
}
return next;
}
// 求next数组的改进版本
public static int[] buildeNextImproved(String P) {
int[] next = new int[P.length()];
int j = 0;
int t = -1;// 初始值是-1
next[0] = -1;
while (j < P.length() - 1) {
if (t == -1 || P.charAt(t) == P.charAt(j)) {// 匹配---如果在j这里不存在首尾相同的字符串,那么next[j]就会等于0
t++;
j++;
next[j] = (P.charAt(j) != P.charAt(t)) ? t : next[t];// 改进的地方
} else {// 不匹配
t = next[t];
}
}
return next;
}
}
附上参考书籍的对应章节pdf下载地址:http://115.com/file/dpkg6art#数据结构与算法(Java_描述).pdf
浙公网安备 33010602011771号