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 ) );
		}
	}
}


原文地址:https://www.cnblogs.com/wiklvrain/p/8179337.html