大话数据结构(十二)java程序——KMP算法及改进的KMP算法实现

1、朴素的模式匹配算法

朴素的模式匹配算法:就是对主串的每个字符作为子串开头,与要连接的字符串进行匹配。对主串做大循环,每个字符开头做T的长度的小循环,直到成功匹配或全部遍历完成为止。

又称BF算法

package com.alice.dataStructe5.bf;

/**
* 返回子串t在主串s中第pos个字符之后的位置,若不存在,则返回-1
* @author Administrator
*
*/
public class BF { public int index(String s,String t){ char ch1[] =s.toCharArray();
char ch2[] = t.toCharArray();
int i = 0;//主串的pos位置开始
int j = 0;//j用于子串开始的下表
while(i<ch1.length && j<ch2.length){//若i小于s长度且j小于t长度时循环
if(ch1[i]==ch2[j]){//两个字母相等则继续
i++;
j++;
}else{//指针后退重新开始匹配
i = i-j+1;//i退回到上次匹配首位的下一个位置
j=0;//退回到子串t的首位
}
}
if(j==ch2.length){
return i-j+1;//返回子串t在主串s中第pos个字符之后的位置
}else{
return -1;//不存在,则返回-1
} } public static void main(String args[]){
BF bf = new BF(); System.out.println(bf.index("hellolo", "lo"));
System.out.println(bf.index("goooglegoogle", "goog")); } }

2、KMP模式匹配算法

这个算法,可以大大避免重复遍历的情况。

在朴素的模式匹配算法中,主串的i值是不断回溯的。KMP 模式匹配算法就是为了让这种没有必要的回溯不发生。

KMP模式匹配算法的重要点在于,寻找next数组。

next数组:当模式匹配中T失配时,next数组对应的元素指定应用T串的哪个元素进行下一轮匹配。

KMP模式匹配算法的实现代码:

package com.alice.dataStructe5.kmp;

import java.util.Arrays;

public class KMP {
public int[] getNext(char[] ch){
int i = 0;//后缀
int j = -1;//前缀
int[] next = new int[ch.length];
next[0] = -1;
while(i < ch.length-1){
if( j == -1|| ch[i]==ch[j]){//ch[i]表示后缀的单个字符,ch[j]表示前缀的单个字符
i++;
j++;
next[i] = j;
}else{
j = next[j];//若字符不相等,j值回溯
}
}
System.out.println(Arrays.toString(next));
return next;
}
public void getNext1() {
int j = 0, k = -1;
String s="ababaaaba";
char []c=s.toCharArray();
int[] next1 = new int[c.length];
next1[0] = -1;
while (j < c.length - 1) {
if (-1 == k || c[j] == c[k]) {
j++;
k++;
next1[j] = k;
} else {
k = next1[k];
}
}
System.out.println(Arrays.toString(next1)); } public int Index_KMP(String str, String sub) {
char[] ch1 = str.toCharArray();
char[] ch2 = sub.toCharArray();
int i = 0;
int j = 0;
int index = 0;
int[] nextArray = null;
nextArray = getNext(ch2);
while(i <ch1.length && j<ch2.length){
if(ch1[i] == ch2[j] || j==0){
i++;
j++;
}else{
j = nextArray[j];
}
}
if(j >= ch2.length){
index = i-ch2.length;
}else{
index = -1;
}
return index;
}
public static void main(String args[]){
KMP kmp = new KMP();
// kmp.Index_KMP("abcabx", "ab");
String str = "abcabx";
char[] ch = str.toCharArray();
kmp.getNext(ch);
int a=kmp.Index_KMP("aaaabcdefaaaaax", "aaaaax");
System.out.println("a的值为:"+a);
// kmp.getNext1();
// System.out.println(kmp.Index_KMP("goooglegoogle", "goog"));
System.out.println(kmp.Index_KMP("ababcabcacbab", "abcac"));
}
}

KMP算法的时间复杂度为O(N+M),相对于模式匹配算法O((n-m+1)*m)来说,更好一些

3、改进的KMP算法

package com.alice.dataStructe5.kmp;

import java.util.Arrays;

public class KMPImproved {

    /**
* 改进的KMP算法中,得到nextval数组的方法,用于指导下个元素进行下一轮匹配
* @param ch子串数组
* @return
*/
public int[] getNextval(char[] ch){
int j = -1;
int i = 0;
int[] nextval = new int[ch.length];
nextval[0] = -1;
while(i < ch.length-1){
if(j==-1 || ch[i] == ch[j]){//ch[i]表示后缀的单个字符,ch[j]表示前缀的单个字符
i++;
j++;
if(ch[i] != ch[j]){//若当前字符与前缀字符不匹配
nextval[i] = j;//则当前的j为nextval在i位置的值
}else{
nextval[i] = nextval[j];//如果与前缀字符相等,则将前缀字符的nextval赋值给nextval在i位置的值
}
}else{
j = nextval[j];
}
}
System.out.println(Arrays.toString(nextval));
return nextval;
}
public int getKMP(String s,String t){
char[] ch1 = s.toCharArray();//将字符串转为数组
char[] ch2 = t.toCharArray();//将字符串转为数组
int i = 0;
int j = 0;
int index = 0;
int[] nextval = getNextval(ch2);//定义数组nextval[],通过getNextval()方法得到
while(i < ch1.length && j < ch2.length){//若i小于ch1的长度且j小于ch2的长度,循环继续
if(j==0 || ch1[i] == ch2[j]){//两字符相等,则继续
i++;
j++;
}else{//指针后退重新进行下一轮匹配
j = nextval[j];//j值退回到合适的位置,i值不变
}
}
if(j >= ch2.length){
index = i-ch2.length;//返回子串第一次出现在主串的位置
}else{
index = -1;
}
return index;
}
public static void main(String args[]){
KMPImproved kmpImproved = new KMPImproved();
String t = "ababaaaba";
kmpImproved.getNextval(t.toCharArray());
System.out.println(kmpImproved.getKMP("ababcabcacbab", "abcac"));;
}
}

  

上一篇:tomcat管理员在远程(不同)机器*问管理页面


下一篇:Elias-Fano编码算法——倒排索引压缩用,本质上就是桶排序数据结构思路