zoukankan      html  css  js  c++  java
  • KMP算法

    KMP算法(Knuth-Morris-Pratt Algorithm)是一种非常高效的字符串匹配算法,是由Knuth,Morris和Pratt三位与1977年发布的算法。最坏复杂度为O(n+m)


    首先我们用一个例子来演示这个算法:

    原串为babababcbababababb

    模式串为bababb

    模式串的失配数组为0,1,1,2,3,4


    i = 6, j = 6时,出现了第一次不匹配,于是获取到失配指针fail[j],使j = fail[j]继续进行比较,即此时的i = 6, j = fail[j] = 4。如下图所示:


    结果发现在i = 8, j = 6的时候,再次失配,于是j再次赋值为失配指针数值。依次匹配,发现最后当i = 8,j = 1的时候仍然无法匹配,则j = 0,如下图:


    循环往复,直到得到最后的结果:



    可以直观的发现,其实KMP算法的精髓就在于失配指针上面。

    那么什么是失配指针呢?如何获得失配指针的值呢?

    首先,失配指针就是当匹配失败时,所能跳转到的最近的位置。换种说法,失配指针的值就是模式串的[1..j]子串的前缀与后缀的最大匹配值+1。

    所以获取这个的值就可以这么写:

    fail[j] = max{k | pattern.substring(1...k - 1) == pattern.substring(j - k + 1, j)};

    根据这个公式,于是我们可以获得示例中的模式串的失配数组的取值。


    在匹配的过程中,就按照示例中的匹配方式一样,如果匹配相同的就继续向下匹配,如果匹配到失败,就使得j = fail[j]继续匹配。然后就可以得到匹配的结果了。


    KMP算法Java代码如下:

    public static void getNext( String pattern ) {
    	fail = new int[ pattern.length() ];
    	fail[0] = -1;
    	for( int i = 0, j = -1, len = pattern.length(); i < len - 1; ++i, ++j ) {
    		while( j != -1 && pattern.charAt(i) != pattern.charAt(j) ) j = fail[j];
    		fail[i + 1] = j + 1;
    	}
    }
    public static int kmp( String pattern, String origin ) {
    	getNext( pattern );
    	int ans = 0;
    	for( int i = 0, j = 0, len = origin.length(), lenP = pattern.length(); i < len; ++i, ++j ) {
    		while( j != -1 && pattern.charAt(j) != origin.charAt(i) ) j = fail[j];
    		if( j == lenP - 1 ) {
    			++ans;
    			j = fail[j];
    			while( j != -1 && pattern.charAt(j) != origin.charAt(i) ) j = fail[j];
    		}
    	}
    	return ans;
    }

    附上hihocoder-1015-kmp算法ac代码:

    import java.io.BufferedInputStream;
    import java.io.IOException;
    import java.util.Scanner;
    
    public class Main {
    	/**
    	 * @param args
    	 * @author wiklvrain
    	 */
    	
    	static int[] fail;
    	
    	public static void getNext( String pattern ) {
    		fail = new int[ pattern.length() ];
    		fail[0] = -1;
    		for( int i = 0, j = -1, len = pattern.length(); i < len - 1; ++i, ++j ) {
    			while( j != -1 && pattern.charAt(i) != pattern.charAt(j) ) j = fail[j];
    			fail[i + 1] = j + 1;
    		}
    	}
    	public static int kmp( String pattern, String origin ) {
    		getNext( pattern );
    		int ans = 0;
    		for( int i = 0, j = 0, len = origin.length(), lenP = pattern.length(); i < len; ++i, ++j ) {
    			while( j != -1 && pattern.charAt(j) != origin.charAt(i) ) j = fail[j];
    			if( j == lenP - 1 ) {
    				++ans;
    				j = fail[j];
    				while( j != -1 && pattern.charAt(j) != origin.charAt(i) ) j = fail[j];
    			}
    		}
    		return ans;
    	}
    	public static void main(String[] args) throws IOException {
    		// TODO Auto-generated method stub
    		Scanner in = new Scanner( new BufferedInputStream(System.in) );
    		
    		int n = Integer.parseInt( in.nextLine() );
    		while( n-- > 0 ) {
    			String pattern = in.nextLine();
    			String origin = in.nextLine();
    			System.out.println( kmp( pattern, origin ) );
    		}
    	}
    }


  • 相关阅读:
    怎样快速学会ZBrush 中的移动笔刷的运用
    ZBrush中如何才能快速完成脸部雕刻(下)
    ZBrush中如何才能快速完成脸部雕刻(上)
    ZBrush中的Clip剪切笔刷怎么快速运用
    ZBrush中必须记住的常用快捷键
    怎么在ZBrush中渲染漫画风格的插画
    怎么运用ZBrush中的Z球制作身体部分
    ZBrush中的笔刷该怎样制作
    如何在ZBrush中添加毛发
    App交互demo
  • 原文地址:https://www.cnblogs.com/wiklvrain/p/8179337.html
Copyright © 2011-2022 走看看