KMP算法的介绍及其Java实现


这里有一篇好文章:http://www.cnblogs.com/c-cloud/p/3224788.html


问题:给定一个字符串,寻找在一个长字符串中有没有与给定字符串匹配的子串。


解决这个问题,称之为字符串匹配算法。当前比较经典的一个算法是KMP算法。具体过程如下:


1)给定两个字符串,从第一位开始对比。当第一位不同时,向后遍历




2)当第一位相同时,继续向后遍历





当遍历到后面时,发现不匹配了。此时就需要继续向后遍历,传统的方式是向后跳一位,但是这就造成了前面成功遍历的计算量的浪费。

KMP算法就是在当前情形的时候,寻找下一跳跳转的位数。


KMP算法是:跳转位数=已匹配的字符数-对应的部分匹配值

上面的例子中,已匹配的字符数为3,已匹配的字符串ABC的部分匹配值为0,所以跳转位数就是 3 - 0 = 3 ,继续向后遍历

至于字符串的部分匹配值,先别care,下面会介绍。


3)跳转之后,按照前面说的准则来做匹配







当前匹配完成一次,继续向后面做匹配,此时已匹配字符数为 4, 而已匹配的字符串ABCD的部分匹配值为0,跳转位数为4.




不匹配,继续向下,而此时已经到了字符串尾,这样一次遍历就完成了。



下面来讲字符串的部分匹配值的计算


字符串的部分匹配值就是当前字符串的前缀字符串集 和 后缀字符串集的公共字符串的最长的长度。


举个例子  ABCDAB

前缀集:A  AB  ABC  ABCD  ABCDA

后缀集:B  AB  DAB  CDAB  BCDAB

所以最长公共字符长度为2,所以这个字符串的部分匹配值就是2


再举个例子  ABCDA

前缀集:A  AB  ABC  ABCD 

后缀集:A  DA  CDA  BCDA

最长公共字符串长度为1,所以这个字符串的部分匹配值就是1



最后再来讲代码实现吧,这里用java实现

import java.util.Arrays;

/**
 * KMP算法的Java实现
 */
public class KMP {

	private int[] nextArr = null;
	private String originStr = null;
	private String moduleStr = null;

	public KMP(String originStr, String moduleStr) {
		this.originStr = originStr;
		this.moduleStr = moduleStr;
		this.nextArr = caculate_nextArr(moduleStr);
	}

	/**
	 * 计算next数组的值(部分匹配值)
	 * */
	private int[] caculate_nextArr(String str) {
		if (str == null || str.length() == 0) {
			return null;
		}
		int[] theNextArr = new int[str.length()];

		for (int i = 0; i < str.length(); i++) {
			if (i == 0) {
				theNextArr[i] = 0;
			} else if (i == 1) {
				if (str.charAt(0) == str.charAt(1)) {
					theNextArr[i] = 1;
				} else {
					theNextArr[i] = 0;
				}
			} else {
				int theLength2 = i;
				boolean hasEqual = false;

				for (int j = theLength2 - 1; j >= 0; j--) {
					String prefix_str = str.substring(0, j + 1);
					String suffix_str = str.substring(theLength2 - j,
							theLength2 + 1);
					if (prefix_str.equals(suffix_str)) {
						hasEqual = true;
						theNextArr[i] = prefix_str.length();
						break;
					}
				}
				if (hasEqual == false) {
					theNextArr[i] = 0;
				}
			}
		}
		return theNextArr;
	}

	/**
	 * 获取子字符串在父字符串中的位置
	 */
	public int getIndexOfStr() {

		if (moduleStr == null || moduleStr.length() <= 0) {
			return -1;
		}
		if (originStr == null || originStr.length() <= 0) {
			return -1;
		}
		if (originStr.length() < moduleStr.length()) {
			return -1;
		}
		int res = -1;
		int totalLength = originStr.length();

		int origin_loc = 0;
		int module_loc = 0;
		while (true) {

			char c_origin = originStr.charAt(origin_loc);
			char c_module = moduleStr.charAt(module_loc);

			if (c_origin == c_module) {
				if (module_loc == moduleStr.length() - 1) {
					res = origin_loc - module_loc;
					break;
				} else {
					origin_loc++;
					module_loc++;
				}
			} 
			else {
				if (module_loc == 0) {
					origin_loc++;
				} else {
					module_loc=nextArr[module_loc-1];  
				}
			}
			
			if (origin_loc >= totalLength) {
				break;
			}
		}
		return res;
	}
	
	// 测试
	public static void main(String[] args) {
		KMP ktest = new KMP("BBC ABCDAB ABCDABCDABDE", "ABDE");
		System.out.println("部分匹配值的next数组:");
		System.out.println(Arrays.toString(ktest.nextArr));
		int theLoc = ktest.getIndexOfStr();
		System.out.println();
		System.out.println("匹配位置在:" + theLoc);
	}
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值