【发布时间】:2012-12-31 23:17:08
【问题描述】:
我设计了一个算法来找到最长的公共子序列。这些是步骤:
选择第一个字符串中的第一个字母。
在第二个字符串中查找它,如果找到,将该字母添加到
common_subsequence并将其位置存储在index中,否则 比较common_subsequence的长度和lcs的长度 如果更大,则将其值分配给lcs。返回第一个字符串并选择下一个字母并重复 再上一步,不过这次从
indexth letter开始搜索重复此过程,直到第一个字符串中没有字母 挑选。最后
lcs的值是最长公共 子序列。
这是一个例子:
X=A, B, C, B, D, A, B
Y=B, D, C, A, B, A
在第一个字符串中选择A。
在Y 中寻找A。
现在第二个字符串中有一个A,将其附加到common_subsequence。
返回第一个字符串并选择下一个字母B。
这次从A的位置开始,在第二个字符串中寻找B。
在A 之后有一个B,所以将B 附加到common_subsequence。
现在选择第一个字符串中的下一个字母C。第二个字符串中的B 旁边没有C。所以将common_subsequence的值赋值给lcs,因为它的长度大于lcs的长度。
重复前面的步骤,直到到达第一个字符串的末尾。最后lcs的值就是最长公共子序列。
这个算法的复杂度是theta(n*m)。 这是我的实现:
第一个算法:
import time
def lcs(xstr, ystr):
if not (xstr and ystr): return # if string is empty
lcs = [''] # longest common subsequence
lcslen = 0 # length of longest common subsequence so far
for i in xrange(len(xstr)):
cs = '' # common subsequence
start = 0 # start position in ystr
for item in xstr[i:]:
index = ystr.find(item, start) # position at the common letter
if index != -1: # if common letter has found
cs += item # add common letter to the cs
start = index + 1
if index == len(ystr) - 1: break # if reached end of the ystr
# update lcs and lcslen if found better cs
if len(cs) > lcslen: lcs, lcslen = [cs], len(cs)
elif len(cs) == lcslen: lcs.append(cs)
return lcs
file1 = open('/home/saji/file1')
file2 = open('/home/saji/file2')
xstr = file1.read()
ystr = file2.read()
start = time.time()
lcss = lcs(xstr, ystr)
elapsed = (time.time() - start)
print elapsed
使用哈希表的相同算法:
import time
from collections import defaultdict
def lcs(xstr, ystr):
if not (xstr and ystr): return # if strings are empty
lcs = [''] # longest common subsequence
lcslen = 0 # length of longest common subsequence so far
location = defaultdict(list) # keeps track of items in the ystr
i = 0
for k in ystr:
location[k].append(i)
i += 1
for i in xrange(len(xstr)):
cs = '' # common subsequence
index = -1
reached_index = defaultdict(int)
for item in xstr[i:]:
for new_index in location[item][reached_index[item]:]:
reached_index[item] += 1
if index < new_index:
cs += item # add item to the cs
index = new_index
break
if index == len(ystr) - 1: break # if reached end of the ystr
# update lcs and lcslen if found better cs
if len(cs) > lcslen: lcs, lcslen = [cs], len(cs)
elif len(cs) == lcslen: lcs.append(cs)
return lcs
file1 = open('/home/saji/file1')
file2 = open('/home/saji/file2')
xstr = file1.read()
ystr = file2.read()
start = time.time()
lcss = lcs(xstr, ystr)
elapsed = (time.time() - start)
print elapsed
【问题讨论】:
-
在 Python 中按索引循环几乎总是一个糟糕的想法——通常有更好的方法。此外,空字符串的计算结果为
None,因此不要检查len(str)是否为0,只需检查not str。 -
我在这里推荐动态编程。查找wikipedia
-
动态编程的下限比你的低,执行的比较也比你的少(即使它有相同的大 O 比较)。因此,实际运行时会更快,即使它属于同一个复杂度等级
-
@Rastegar:你的教授想让你发明一种新算法?或者只是为了实现一些好的算法?如果您对研究问题感兴趣,而不仅仅是您的程序需要什么,请参阅 Bergroth、Hakonen 和 Raita 的 A survey of longest common subsequence algorithms,这将为您提供所需的背景知识。
-
@Rastegar:嗯,你总是可以去图书馆拿死树形式的日记,然后从那里复制。 :)