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

  

 

posted on 2016-11-23 16:35  小企鹅ITgo  阅读(275)  评论(0编辑  收藏  举报