解法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
参考资料: