字串匹配理解

2021-08-25 22:49:01 字數 3525 閱讀 7817

對我乙個菜鳥而言,理解字串匹配優化演算法著實有些難,看了很多他人的理解。

自己終於稍稍有些理解。趕緊自己總結了一下

對於字串匹配一直是乙個熱點,首先我們可以使用暴力法進行比較,當找到了完全

匹配的序列,則返回對應序列的首字母位置。

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的**

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)

#

3:kmp演算法

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中保證不出現...