字串匹配演算法(KMP、BM和Sunday),及Python實現
阿新 • • 發佈:2019-02-06
主要對三種字串匹配演算法(KMP、BM、Sunday)進行總結。這三種字串匹配演算法之間的主要區別在於:如果在匹配過程中遇到一個不匹配位,該用何種策略進行移位。例如,存在兩個字串,如下:
Sunday演算法預期完全不同。同樣是上面的例子當搜到不匹配的字串時,Sunday演算法採用了一種完全不同的以為確定法。它會先找到字串的第K+1個字元,K是搜尋字元的長度。如果搜尋字串中不包含字串中第K+1個字元,則直接移動K+1位。否則,按著BM演算法移動搜尋串中最右端的該字元到末尾的距離+1位。
字串: ABCADAB ABCDABCDABD
搜尋字串:ABCDA
下面給出三種演算法的例子
KMP:在此演算法中當從前往後搜尋時遇到第一個不匹配:A->D時,它將從搜尋字串入手決定移動多少位。在KMP演算法的初始階段會生成一張表,例如,上面搜尋字串生成的表為:pi[0,...,4] = {0,0,0,0,1}。這張表決定上面提到的移位。此時的移位為:3-pi[2](因為已經匹配了三個字元)。KMP演算法的關鍵就是:匹配字元的初始生成表,而且是從前往後進行搜尋。
BM:在此演算法中,字串搜尋不是從頭開始的,而是從末尾開始的,例如上面的例子中,首先比較的是D和A,因為不相同,則在搜尋字元中從後往前進行匹配查詢,找到最右邊的匹配字元後進行移位,如果找不到的話移位長度與匹配字元一樣長,如下:
字串: ABCADAB ABCDABCDABD
搜尋字串: ABCDA(移兩位)
此時繼續進行比較,不過此時的比較要考慮兩方面以上上面提到的過程,還有一種情況就是已匹配的字串中(DA),包含了搜尋字串的字首(A),我們知道此時移動1~3位是沒有意義的。所以BM演算法的關鍵就是找到兩種移位中的最大移位,進行以為。
Sunday:上面的兩種字串匹配演算法都涉及到了對搜尋字元的預處理,但
class StringPatternt(object): def __init__(self,chr,p): self.chr = chr; self.p = p; self.p_len = len(p); self.pi = [0 for i in range(self.p_len)]; def set_pattern(self,p): self.p = p; self.p_len = len(p); def set_chr(self,chr): self.chr = chr; '''KMP''' def __kmp_partial_match_table__(self): k=0;q = 1; #self.pi[0] = 0; while q < self.p_len: while (k > 0) and (self.p[k] != self.p[q]): k = self.pi[k-1]; if self.p[k] == self.p[q]: k = k+1; self.pi[q] = k; q = q+1; return 0; def string_pattern_kmp(self): self.__kmp_partial_match_table__(); print(self.pi); list_size = len(self.chr); pi_len = len(self.pi); k=0; for q in range(list_size): while (k > 0) and (self.p[k] != self.chr[q]): k = self.pi[k-1]; if self.p[k] == self.chr[q]: k = k+1; if k == pi_len: return q-pi_len+1; #q = q+1; return 0; '''BM''' def __calc_match__(self,num): k=num;j=0; while k>=0: if self.p[-k] == self.p[j]: k = k-1; j=j+1; if k<=0: self.pi[num-1] = num; return 0; else: if num == 1: return 0; self.pi[num-1] = self.pi[num-2]; return 0; def __init_good_table__(self): i=1; while i <= self.p_len: self.__calc_match__(i); i=i+1; print (self.pi); return 0; def __check_bad_table__(self,tmp_chr): i=1; while self.p_len-i >= 0: if self.p[-i] == tmp_chr: return i; else: i = i+1; return self.p_len+1; def __check_good_table__(self,num): if not num: return self.p_len; else: return self.pi[num]; def string_pettern_bm(self): self.__init_good_table__(); tmp_len = self.p_len; i = 1; while tmp_len <= len(self.chr): if self.p[-i]==self.chr[tmp_len-i]: i = i+1; if i > self.p_len: return tmp_len-self.p_len; else: tmp_bad = self.__check_bad_table__(self.chr[tmp_len-i])-i; tmp_good= self.p_len-self.__check_good_table__(i-1); tmp_len = tmp_len+ max(tmp_bad,tmp_good); print(tmp_bad,tmp_good,tmp_len); i=1; return 0; '''sunday''' def __check_bad_shift__(self,p): i=0; while i<self.p_len: if self.p[i] == p: return i; else: i = i+1; return -1; def string_pattern(self): #self.__init_good_table__(); tmp_len = 0; tmp_hop = self.p_len; i=0; while tmp_hop <= len(self.chr): if self.p[i] == self.chr[tmp_len+i]: i = i+1; if i == self.p_len: return tmp_len; else: tmp_len = tmp_len+self.p_len-self.__check_bad_shift__(self.chr[tmp_hop]); tmp_hop = tmp_len+self.p_len; i=0; return 0;