主要对三种字符串匹配算法(KMP
、
BM
、
Sunday)
进行总结。这三种字符串匹配算法之间的主要区别在于:如果在匹配过程中遇到一个不匹配位,该用何种策略进行移位。例如,存在两个字符串,如下:
字符串: 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:上面的两种字符串匹配算法都涉及到了对搜索字符的预处理,但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;