标签:位置 角度 blog 分享 字符串匹配 static array 字符串前缀 index
首先是简单的朴素匹配算法
/* * 返回子串t在主串s的位置,若不存在则返回0 */ public static int index(String s, String t) { int i = 0;//i记录主串当前位置的下标 int j = 0; //j记录子串当前位置的下标 char[] c = t.toCharArray(); char[] d = s.toCharArray(); while(i < s.length() && j < t.length()) { if(c[i] == d[j]) { i++; j++; if(j == t.length()) { break; } }else { i = i - j;//如果不匹配i退回到上次匹配首位的下一位 j = 0; } } if(j == t.length()) { return i - j + 1; } return 0; }
举例说明:
s是 abcabcabd t是 abcabd,朴素的匹配算法每次发现不对都要重新回到上次匹配的首位,也就是要重新在s从找一次t的和第一个字符匹配的字符。
但是像这个例子t字符串中一开始就有ab后面也有ab,也就是说如果匹配到最后一位发现不匹配的时候,就可以直接进行到这里
所以这就是kmp改进的地方,先自行处理一下t字符串,找到t字符串中与前缀有重复的。建立一个next数组记录下这个重复,就比如这个t匹配到最后一个时发现s[5]和t[5]匹配失败,但是t[3]t[4]和t[1]t[2]是重复的,所以这里可以直接进行s[5]和t[3]的匹配。从代码的角度讲就是当i=5,j=5时,s[i]和t[j]匹配失败,那就修改j跳过之前与t字符串前缀重复的。
这是一个next数组,这里j=0时没有前缀所以在匹配的时候就应该重t[0]开始匹配,j=1时前缀是a但是t[1]是b所以没有重复,所以还是0,一直到j=3,a和前缀a重复所以所以在匹配的时候就应该重t[1]开始匹配,到j=4,ab和前缀ab重复所以所以在匹配的时候就应该重t[2]开始匹配,j=5的时候abd和前缀abc并没有重复所以应该重t[0]开始匹配。
再然后就是这个next数组怎么计算。。
声明一个k初始化为0用于记录前缀被重复的长度了,就比如t[3]和t[0]重复就记1,此时next[]相应的位置就是1(k),t[3]t[4]和t[0]t[1]重复就记2,此时此时next[]相应的位置就是2(k),t[3]t[4]t[5]t[0]t[1]t[3不]重复就置0。
整体代码:
public static int KmpIndex(String s, String t) { int i = 0; int j = 0; //j记录子串当前位置的下标 int[] next = new int[t.length()]; char[] c = t.toCharArray(); char[] d = s.toCharArray(); int k = 0; while(j < t.length()) { if(k == 0 && j == 0) { //第一个字符的前后缀为空 next[j++] = 0; }else { //使用k来记住已经和前者匹配到那个位置了 if(k == 0 ) { //k=0 就是上一个未匹配 if(c[k] != c[j]) { //不相等就是不匹配 next[j++] = 0; }else { //相等就是匹配,next[j]等于k+1,然后增加k和j的值 next[j++] = ++k; } }else { //k!=0 就是上k个都已经匹配了 if(c[k] != c[j]) { //不相等就是不匹配而且之后要重头匹配 k=0 next[j++] = 0; k = 0; }else { //相等就是匹配,next[j]等于k+1,然后增加k和j的值 next[j++] = ++k; } } } } j = 0; while(i < s.length() && j < t.length()) { if(d[i] == c[j]) { i++; j++; if(j == t.length()) { break; } }else { j = next[j]; } } if(j == t.length()) { return i - j; } return 0; }
标签:位置 角度 blog 分享 字符串匹配 static array 字符串前缀 index
原文地址:http://www.cnblogs.com/xxbbtt/p/7625726.html