现在的位置: 首页 > 综合 > 正文

KMP算法学习

2012年02月01日 ⁄ 综合 ⁄ 共 4165字 ⁄ 字号 评论关闭

(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

抱歉!评论已关闭.