每天一道算法題——字符串匹配

字符串匹配算法是我在公司面試時候的一道算法題,當時用的還是最基本的暴力枚舉法寫出來的吧,之前看過的KMP算法,Rabin-Karp算法都沒用上,今天就來總結一下字符串匹配的幾種算法吧。

一、暴力匹配法:
這應該是萬能算法了,但性能比較差,它的過程就是在[0,n-m]範圍內,查找是否存在一個s,0<=s<=m,是得P[1..m] = T[s+1,...,s+m]。最壞情況下外層for循環次數爲n-m+1, 內層for循環執行m次,所以算法複雜度是O(m(m-n+1))。
代碼如下:
package p_14_matchstr;

public class MatchString {

	public int match1(String P, String T) {
		for(int i=0; i<T.length()-P.length(); i++) {
			for(int j=0; j<P.length(); j++) {
				if(P.charAt(j) == T.charAt(i+j) && j == P.length()-1) {
					return i;
				}else if(P.charAt(j) != T.charAt(i+j)) {
					break;
				}
			}
		}
		return -1;
	}
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		 String T = "abcabaabcabac";
	     String P = "abaa";
	     RabinKarp rk = new RabinKarp(T, P, 26, 29);
	     MatchString ms = new MatchString();
	     System.out.println(ms.match1(P, T));
	     int s = rk.matchRK();
	     System.out.println("Valid shift is: "+ s);
	}

}


二、進階版 Rabin-Karp版本:該算法在實際運用中,表現不錯,RK算法需要O(m) 時間做預處理,在最壞情況下,該算法的複雜度與枚舉法一樣都是,O((n - m + 1) m).但在實際運用中,最壞情況極少出現。
對於一個長度爲m的字符串P[1…m],用p表示該字符串對應的含有m個數字的整形數,我們用ts 來表示T[s+1, … , s+m] 這m個字符對應的整形數值,不難發現,當兩個數值相等時,這兩個數值對應的字符串就相等,也就是當且僅當p = ts 有 P[1…m] = T[s+1,…,s+m]。
將字符串轉換成數字:p = P[m] + 10(P[m-1]+10(P[m-2]+...+(10P[2]+P[1])...)
如果不是該字符串,計算下一個數ts1,公式:
計算一次的複雜度爲O(1), 計算t0,... ,tn-m是需要O(n - m + 1),所以時間複雜度O(n-m+1)。
但是這個方法有一個缺點,就是如果數非常大的情況下可能會導致溢出。(當兩個過大的數值比較大小時,CPU需要多個運算週期來進行,這樣兩數比較,我們就不能假定他們可以在單位時間內完成了。處理這種情況的辦法可以採用求餘。但是p,ts求餘後又會引入新問題:數值相等但是字符串不匹配。這就需要在兩個求餘後的數相等的情況下,再去逐個匹配每個字符。
代碼實現:
package p_14_matchstr;

public class RabinKarp {

	private String T, P;
	private int d, q;
	private int n, m;
	private int k = 1;
	
	public RabinKarp(String T, String P, int d, int q) {
		this.T = T;
		this.P = P;
		this.d = d;
		this.q = q;
		n = T.length();
		m = P.length();
		
		for(int i=0; i<m; i++){
			k = k*d;
			k %= q;
		}
	}
	
	public int matchRK(){
		int p = 0;
		int t = 0;
		
		for(int i=0; i<m; i++){
			p = (d*p + (P.charAt(i) - 'a')) % q;
			t = (d*t + (P.charAt(i) - 'a')) % q;
		}
		
		for(int i=0; i<n-m; i++){
			if(p == t){
				for(int j=0; j<m; j++){
					if(j == m-1 && P.charAt(j) == T.charAt(j+i)) {
						return i;
					}else if(P.charAt(j) != T.charAt(j+i)){
						break;
					}
				}
			}else{
				t = (d*(t-k*(T.charAt(i)-'a'))+T.charAt(i+m)-'a')% q;
				if(t < 0){
					t += q;
				}
			}
		}
		return -1;
	}
}
	



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章