1. 程式人生 > >字串匹配演算法(KMP、BM和Sunday),及Python實現

字串匹配演算法(KMP、BM和Sunday),及Python實現

主要對三種字串匹配演算法(KMPBMSunday)進行總結。這三種字串匹配演算法之間的主要區別在於:如果在匹配過程中遇到一個不匹配位,該用何種策略進行移位。例如,存在兩個字串,如下:

字串:      ABCADAB ABCDABCDABD

搜尋字串:ABCDA

下面給出三種演算法的例子

KMP:在此演算法中當從前往後搜尋時遇到第一個不匹配:A->D時,它將從搜尋字串入手決定移動多少位。在KMP演算法的初始階段會生成一張表,例如,上面搜尋字串生成的表為:pi[0,...,4] = {0,0,0,0,1}。這張表決定上面提到的移位。此時的移位為:3-pi[2](因為已經匹配了三個字元)。KMP演算法的關鍵就是:匹配字元的初始生成表,而且是從前往後進行搜尋。

BM:在此演算法中,字串搜尋不是從頭開始的,而是從末尾開始的,例如上面的例子中,首先比較的是DA,因為不相同,則在搜尋字元中從後往前進行匹配查詢,找到最右邊的匹配字元後進行移位,如果找不到的話移位長度與匹配字元一樣長,如下:

字串:      ABCADAB ABCDABCDABD

搜尋字串:   ABCDA(移兩位)

此時繼續進行比較,不過此時的比較要考慮兩方面以上上面提到的過程,還有一種情況就是已匹配的字串中(DA),包含了搜尋字串的字首(A),我們知道此時移動1~3位是沒有意義的。所以BM演算法的關鍵就是找到兩種移位中的最大移位,進行以為。

Sunday:上面的兩種字串匹配演算法都涉及到了對搜尋字元的預處理,但

Sunday演算法預期完全不同。同樣是上面的例子當搜到不匹配的字串時,Sunday演算法採用了一種完全不同的以為確定法。它會先找到字串的第K+1個字元,K是搜尋字元的長度。如果搜尋字串中不包含字串中第K+1個字元,則直接移動K+1位。否則,按著BM演算法移動搜尋串中最右端的該字元到末尾的距離+1位。


    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;