1.暴力法對我乙個菜鳥而言,理解字串匹配優化演算法著實有些難,看了很多他人的理解。
自己終於稍稍有些理解。趕緊自己總結了一下
對於字串匹配一直是乙個熱點,首先我們可以使用暴力法進行比較,當找到了完全
匹配的序列,則返回對應序列的首字母位置。
設定兩個索引指標i,j分別初始為0,若s[i]==p[j],則同時加1,若是s[i]!=p[j]
則,i後退匹配時的元素加1,j=0.重複此操作,直到j==p的長度。則匹配成功。
2.rabinkarp演算法
此演算法其實是將源字串每次掃瞄時的n個字串進行計算hash。
這樣不好表述。比如我們源字串長度為10,匹配字串為4.
那我們首先將源字串前0-3中的四個字元進行hash計算。然後整體對1-4的四個字元進行hash計算。直到將源字串所有長度為4的可能字串的hash值計算完畢
繼續將匹配字串計算出hash值。
hash值計算方法:
假設p=』abcd』
hash=』a』*31^3+』b』*31^2+』c』*31^1+』d』
=(((0+』a』)*31+b)*31+』c』)*31+』d』
#此時的字母的值其實是對應的二進位制編碼ord(c)
注:hash值運算其實就是將乙個字串通過某種演算法變成乙個數字,這個數字在很大範圍內幾乎不會重複,但凡事總有例外。重複時的解決辦法後面講解。
當然,對於字串進行hash計算的預處理中,源字串計算過程中同樣達到了o(m*n)的時間複雜度。因為此時源字元每次處理的時間為o(n),共計算大概m次
這樣的複雜度打不到我們的優化目的。
大牛便再想想,能不能讓hash值計算達到o(1)。
於是提出了乙個叫滾動hash。
滾動hash
其實就是在計算第乙個hash源字串之後,向前推進一位的同時,扣除最後一位,得到新的字串 hash值,這樣在預處理字串的hash值只需要o(m + n)
m是源字串所花費的時間,n是匹配字串所花費的時間。最後進行m次比較hash值則可以找出所有匹配的字串位置
總的時間複雜度達到o(2m+n)。達到了優化的目的
下面是**rabinkarp的**
3:kmp演算法def
get_hash
(s,n):
res= #儲存hash值
temp=ord(s[0])
seed=31
for i in range(1,n):
temp=(temp)*seed+ord(s[i]) #儲存第乙個n長字串的hash值
for i in range(n,len(s)): #進行滾動hash。每前進乙個字元,就扣掉扣掉最後乙個字元。
#即等於加上新字元並乘以seed,然後減去老字元*seed**n
print(res)
return res
defmatch
(hash1,hash2,n,s,p):
for i,h in enumerate(hash1):
if h==hash2:
k=0for j in range(i,i+n):
if s[j]!=p[k]:
return -1
else:
return i
return -1
if __name__ == '__main__':
s='abababa'
p='aba'
hash1=get_hash(s,len(p))
hash2=get_hash(p,len(p))[0]
index=match(hash1,hash2,len(p),s,p)
print(index)
#
kmp演算法大家在網上也能看到很多多牛的解法,救我自己理解來說,kmp演算法做的就是和匹配字串匹配失敗時,給匹配字串留一些字元,因為我們之前辛辛苦苦比較的捨不得一下子又從頭再來。
後來大牛們各種找規律,發現每次比較時,i指標不用後退,而j指標可以後退到某乙個位置。
這個位置是有一定規律的。具體規律和匹配的字串p有關。
這個陣列的長度恰好等於匹配字串p的長度。我們把這個陣列定義為next,涵義是每當s[i]!=p[j]時,j指標應該回退到p字串中的next[j]位置(下標)。
所以求next[j]就是我們的一大關鍵。
next陣列求法
首先我們要分清楚到底next陣列的涵義。
並且我們可以發現next[j]=k代表了0-j中有前k個字元等於後k個字元。
即k字首等於k個字尾(p[:k]==p[-k:])。
而我們求出next[j]時,需要求next[j+1]時,分兩種情況:
首先next[0]=-1 因為前面沒有字元。
1.當k==-1 or p[k]==p[j]時。(k=next[j])
next[j+1]=next[j]+1
2.當p[k]!=p[j] 則 k=next[k] 繼續判斷這兩步。直到得出值或者k==-1
求出next陣列,整個kmp演算法的精髓也就出來了。
那麼剩下的就是使用了next進行匹配。整個題目的**如下。
因小生愚笨,且是新手,只能理解到這個地步,若有錯誤,請直接指出來。謝謝def
match
(s,p):
i=0j=0
while iand jif j<0
or s[i] == p[j]:
j += 1
i += 1
else:
j=next[j]
return i-j if j == len(p) else -1
defget_next
(p):
#得到next陣列。
#next陣列定義為當j和i不匹配時,j應該在哪個位置和i重新進行比較。
next_local = [0]*len(p)
next_local[0] = -1
i = 0
k = next_local[i]
while i < len(p)-1:
if k == -1
or p[i] == p[k]: #假設已經知道了next[i]陣列,如果知道了p[j]==p[k]。那麼字首的長度會加1
## 注:next陣列會刨去當前的元素。
next_local[i+1] = k+1
k += 1
i += 1
else:
k = next_local[k]
return next_local
if __name__ == '__main__' :
s = 'sdfsdfdsfbababb'
p = 'bababb'
next = get_next(p)
print(next)
print(match(s,p))
字串演算法KMP(字串匹配)板子及理解
下面是求next陣列的板子 void getnext char b 整個過程就相當於對自己和自己用kmp演算法,但是其中乙個自己要在另乙個自己前面乙個元素進行匹配。else k nx k 元素不相等k就回溯,由於j在k前面,所以此時nx k 是已經被算出來了的。下面是kmp演算法的板子 a是主串,b...
字串匹配
題目描述 讀入資料string 然後讀入乙個短字串。要求查詢string 中和短字串的所有匹配,輸出行號 匹配字串。匹配時不區分大小寫,並且可以有乙個用中括號表示的模式匹配。如 aa 123 bb 就是說aa1bb aa2bb aa3bb都算匹配。輸入 輸入有多組資料。每組資料第一行輸入n 1 n ...
字串匹配
time limit 1000ms memory limit 65536k 給定兩個字串string1和string2,判斷string2是否為string1的子串。輸入包含多組資料,每組測試資料報含兩行,第一行代表string1,第二行代表string2,string1和string2中保證不出現...