0x00 引言
文本處理作為計(jì)算機(jī)基本功能之一,在計(jì)算機(jī)中是和計(jì)算處于同樣重要的地位。字符串匹配是文本處理中最基本的功能,也是大家最常使用的功能。關(guān)于字符串匹配輸入部分只有兩個(gè)基本成分:原字符串和模式,首次匹配成功的首字母次出現(xiàn)的位置就是輸出。下面將介紹一些字符串匹配算法,并給出 Python 實(shí)現(xiàn)。簡單的字符匹配為什么會(huì)存在不同的算法?其實(shí)這些算法的主要區(qū)別是在匹配過程中移位的策略不同。一花一世界。
0x01 問題描述
Input:原字符串,模式
Output:首次匹配成功的首字母的位置
0x02 Brute Force
該方法就是暴力破解,搞過安全的會(huì)遇到各種暴力破解。
class StringSinglePattern(object):
def __init__(self, base_str, p):
self.base_str = base_str # 原字符串
self.p = p # 模式(pattern)
self.p_len = len(p)
self.base_str_len = len(base_str)
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_base_str(self, base_str):
self.base_str = base_str
self.base_str_len = len(base_str)
'''Brute Force'''
def brute_force(self):
p = 0
b = 0
if self.p_len > self.base_str_len:
return 0
while b <= self.base_str_len: # can't use for
if self.base_str[b] == self.p[p]:
b += 1
p += 1
else:
b = b - p + 1
p = 0
if p == self.p_len:
return b - p
return 0
0x03 KMP
該算法中的 K 是計(jì)算機(jī)中的一位大牛,感覺計(jì)算機(jī)界有“四大天王”你應(yīng)該認(rèn)識(shí):Knuth,Dijkstra,Church,Turing。
KMP 算法相對(duì)與 Brute Force 來說不是移動(dòng)一位。
前綴函數(shù)
class StringSinglePattern(object):
def __init__(self, base_str, p):
self.base_str = base_str # 原字符串
self.p = p # 模式(pattern)
self.p_len = len(p)
self.base_str_len = len(base_str)
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_base_str(self, base_str):
self.base_str = base_str
self.base_str_len = len(base_str)
def __kmp_partial_match_table__(self):
k = 0
q = 1
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 kmp(self):
self.__kmp_partial_match_table__()
print(self.pi) # next table
pi_len = len(self.pi)
k = 0
for q in range(self.base_str_len):
while (k > 0) and (self.p[k] != self.base_str[q]):
k = self.pi[k - 1]
if self.p[k] == self.base_str[q]:
k = k + 1
if k == pi_len:
return q - pi_len + 1
return 0
0x04 Boyer Moore
BM 算法相對(duì) KMP 來說移動(dòng)了更多的位置。
壞字符移動(dòng)規(guī)則:
好后綴移動(dòng)規(guī)則:
壞字符,好后綴,兩種移位中的最大值,從右到左,
class StringSinglePattern(object):
def __init__(self, base_str, p):
self.base_str = base_str # 原字符串
self.p = p # 模式(pattern)
self.p_len = len(p)
self.base_str_len = len(base_str)
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_base_str(self, base_str):
self.base_str = base_str
self.base_str_len = len(base_str)
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_base_str):
i = 1
while self.p_len - i >= 0:
if self.p[-i] == tmp_base_str:
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 bm(self):
self.__init_good_table__()
tmp_len = self.p_len
i = 1
while tmp_len <= len(self.base_str):
if self.p[-i] == self.base_str[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.base_str[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
0x05 Sunday
一次性能驗(yàn)證的串越長,算法的效率越高,Sunday 算法的跨度比BM算法更大,直接從模式串最后一位K開始驗(yàn)證。
class StringSinglePattern(object):
def __init__(self, base_str, p):
self.base_str = base_str # 原字符串
self.p = p # 模式(pattern)
self.p_len = len(p)
self.base_str_len = len(base_str)
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_base_str(self, base_str):
self.base_str = base_str
self.base_str_len = len(base_str)
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 sunday(self):
tmp_len = 0
tmp_hop = self.p_len
i = 0
while tmp_hop <= len(self.base_str):
if self.p[i] == self.base_str[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.base_str[tmp_hop])
tmp_hop = tmp_len + self.p_len
i = 0
return 0
0x06 Robin-Karp
Hash怎么可以在這種匹配場合不出現(xiàn)勒?
Waitting
0x07 Bitap
Waitting
0x08 圖
0x09 End
學(xué)過很多,卻一直沒有試著去記錄那些軌跡,現(xiàn)在準(zhǔn)備找工作了,花點(diǎn)時(shí)間記錄一點(diǎn)點(diǎn)吧。
看到字符串匹配算法,我就想到很多字符串處理工具:grep,sed,awk,ag,pt,Search Everything。
學(xué)會(huì)這幾個(gè)小程序有很大的作用,所謂一花一世界。