大话数据结构(十二)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"));; } }