Python程式設計實現對2個字串最長的公共子串的多種求解方式,效能測試及優化
阿新 • • 發佈:2018-12-04
解法1-暴力求解法:
def LongestCommonSubstring(FirstString,SecondString): ''' 求最長子串解法1: 以字串1的每個漢字作為起始位置 去字串2中找到能與之匹配的最長長度 將這個長度和記錄的最長長度比較,從而找到最長的子串長度 然後通過字串2的起始位置和最長長度,找到這個子串 FirstString----字串1 SecondString---字串2 Longest--------最長公共子串的長度 ComputeTimes--計算次數,用於對比計算量 ''' FirstStringLenght = len(FirstString) SecondStringLenght = len(SecondString) Longest = 0 ComputeTimes=0 for i in range(FirstStringLenght): for j in range(SecondStringLenght): m,n,Longer = i,j,0 while FirstString[m]==SecondString[n]: ComputeTimes+=1 Longer += 1 if m>FirstStringLenght | n>SecondStringLenght:break m,n = m+1,n+1 if Longer>Longest: ComputeTimes+=1 Longest,SecondStringStartPoint = Longer,j return Longest,SecondString[SecondStringStartPoint:SecondStringStartPoint+Longest],ComputeTimes
- 時間複雜度: ;
- 空間複雜度:
輸出的時間和電腦的狀態有關,所以迴圈呼叫方法1000次,求平均。這樣的結果更加真實。
str1='怎麼理解一家漏水,四家遭殃的情況'
str2='''案件詳情,2015 年 11 月 4 日因廣粵支路 57 弄 11 號 503 室住戶吳根寶,因為灶頭間晚上水龍頭未關緊,
滴水了一晚上,導致 403/303/203/103 室四家均受到影響。當日四家住戶當即找到漏水根源的 503 室,但由於家中無人,敲門無人迴應,
真是一家漏水,四家遭殃啊。'''
In [19]:import time ...:start=time.clock() ...:for i in range(1000): ...: LongestSubstring=LongestCommonSubstring(str1,str2) ...:print((time.clock()-start)/1000) ...:print(LongestSubstring) Out[19]: 0.000311501507480898 ...: (9, '一家漏水,四家遭殃', 80)
結果:
- 耗時: 0.00031173843790838874
- 計算次數:80
解法2-動態規劃求解法:
import numpy as np def LongestCommonSubstring(FirstString,SecondString): ''' 求最長子串解法2: 建立一個以字串1長度+1乘字串2長度+1的矩陣 矩陣中如果矩陣的行i對應的單詞等於列j對應的單詞,那麼就在對應的m[i+1][j+1]位置等於m[i][j]+1 再將m[i+1][j+1]與最大程度比較,從而找到最大值 FirstString----字串1 SecondString---字串2 Longest--------最長公共子串的長度 ComputeTimes--計算次數,用於對比計算量 ''' FirstStringLenght = len(FirstString) SecondStringLenght = len(SecondString) if (FirstStringLenght==0)|(SecondStringLenght)==0: return Longest = 0 ComputeTimes=0 ''' 構建一個矩陣,行數為字串1的長度+1,列數為字串2的長度+1 這裡+1,是為了計算方便,如果不+1,我們需要單獨對第一行,和第一列做一次迴圈, +1後,我們就可以捨去這段迴圈。 主要是為了這個公式 m[i+1][j+1]=m[i][j]+1 服務 ''' m = np.zeros([FirstStringLenght+1,SecondStringLenght+1],dtype=np.int) for i in range(FirstStringLenght): for j in range(SecondStringLenght): if FirstString[i]==SecondString[j]: ComputeTimes+=1 m[i+1][j+1]=m[i][j]+1 if m[i + 1][j + 1] > Longest: ComputeTimes+=1 Longest,SecondStringStartPoint = m[i + 1][j + 1],j+1 return Longest,SecondString[SecondStringStartPoint-Longest:SecondStringStartPoint],ComputeTimes
- 時間複雜度: ;
- 空間複雜度:
測試階段繼續套用之前的str1和str2 依舊迴圈1000次,求平均。
In [19]:import time
...:start=time.clock()
...:for i in range(1000):
...: LongestSubstring=LongestCommonSubstring(str1,str2)
...:print((time.clock()-start)/1000)
...:print(LongestSubstring)
Out[19]: 0.00026308407759279364
...: (9, '一家漏水,四家遭殃', 41)
結果:
- 耗時: 0.00026308407759279364
- 計算次數:41
解法3-動態規劃求解法2(節約記憶體版):
import numpy as np
def LongestCommonSubstring(FirstString,SecondString):
'''
求最長子串解法2:
建立一個以3乘字串2長度+1的矩陣
矩陣中如果矩陣的行i對應的單詞等於列j對應的單詞,那麼就在對應的m[i+1][j+1]位置等於m[i][j]+1
再將m[i+1][j+1]與最大程度比較,從而找到最大值
FirstString----字串1
SecondString---字串2
Longest--------最長公共子串的長度
ComputeTimes--計算次數,用於對比計算量
'''
FirstStringLenght = len(FirstString)
SecondStringLenght = len(SecondString)
if (FirstStringLenght==0)|(SecondStringLenght)==0:
return
Longest = 0
ComputeTimes=0
'''
構建一個矩陣,行數為3,列數為字串2的長度+1
這裡+1,是為了計算方便,如果不+1,我們需要單獨對第一行,和第一列做一次迴圈,
+1後,我們就可以捨去這段迴圈。
主要是為了這個公式 m[cur+1][j+1]=m[pre+1][j]+1 服務
'''
'''
實質上這個只是為了降低記憶體開銷,實質上和2是沒有差別的。
因為我們只需要2列。通過比較m[cur+1][j+1]和Longest的大小
就能確定最長子串的長度。從而記錄下這個長度的起點。
'''
m=np.zeros([3,SecondStringLenght],dtype=np.int) # 只用兩行就可以計算最長子串長度
for i in range(FirstStringLenght):
# 通過且運算計算出當前行和先前行,實質是奇偶性對比
cur,pre = int((i&1)==1),int((i&1)==0)
for j in range(SecondStringLenght):
if FirstString[i]==SecondString[j]:
ComputeTimes+=1
m[cur+1][j+1]=m[pre+1][j]+1
if m[cur+1][j+1]> Longest:
ComputeTimes+=1
Longest,SecondStringStartPoint = m[cur+1][j+1],j+1
return Longest,SecondString[SecondStringStartPoint-Longest:SecondStringStartPoint],ComputeTimes
- 時間複雜度: ;
- 空間複雜度:
測試階段繼續套用之前的str1和str2 依舊迴圈1000次,求平均。
In [19]:import time
...:start=time.clock()
...:for i in range(1000):
...: LongestSubstring=LongestCommonSubstring(str1,str2)
...:print((time.clock()-start)/1000)
...:print(LongestSubstring)
Out[19]: 0.00027920729279321677
...: (9, '一家漏水,四家遭殃', 41)
結果:
- 耗時: 0.00027920729279321677
- 計算次數:41
參考資料: