(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