Python 在包含可能子序列列表的列表中查找可能回文字符串的算法
我有“n”个字符串作为输入,我将这些字符串分成可能的子序列,如下所示 如果输入为:aa、b、aa 我创建一个如下所示的列表(每个列表都有字符串的子序列): 我想在列表中找到回文的组合。 例如,可能的回文是5-aba,aba,aba,aba,aabaa 这可以通过使用以下代码的暴力算法实现:Python 在包含可能子序列列表的列表中查找可能回文字符串的算法,python,algorithm,Python,Algorithm,我有“n”个字符串作为输入,我将这些字符串分成可能的子序列,如下所示 如果输入为:aa、b、aa 我创建一个如下所示的列表(每个列表都有字符串的子序列): 我想在列表中找到回文的组合。 例如,可能的回文是5-aba,aba,aba,aba,aabaa 这可以通过使用以下代码的暴力算法实现: d = [] def isPalindrome(x): if x == x[::-1]: return True else: return False for I in itertools.p
d = []
def isPalindrome(x):
if x == x[::-1]: return True
else: return False
for I in itertools.product(*aList):
a = (''.join(I))
if isPalindrome(a):
if a not in d:
d.append(a)
count += 1
但是当字符串的数量和长度较大时,这种方法会导致超时
有更好的方法解决这个问题吗?第二版
此版本使用名为seen
的集合,以避免多次测试组合
请注意,您的函数isPalindrome()
可以简化为单个表达式,因此我删除了它,并直接进行了测试,以避免不必要的函数调用的开销
import itertools
aList = [['a', 'a', 'aa'], ['b'], ['a', 'a', 'aa']]
d = []
seen = set()
for I in itertools.product(*aList):
if I not in seen:
seen.add(I)
a = ''.join(I)
if a == a[::-1]:
d.append(a)
print('d: {}'.format(d))
第二版
此版本使用名为seen
的集合,以避免多次测试组合
请注意,您的函数isPalindrome()
可以简化为单个表达式,因此我删除了它,并直接进行了测试,以避免不必要的函数调用的开销
import itertools
aList = [['a', 'a', 'aa'], ['b'], ['a', 'a', 'aa']]
d = []
seen = set()
for I in itertools.product(*aList):
if I not in seen:
seen.add(I)
a = ''.join(I)
if a == a[::-1]:
d.append(a)
print('d: {}'.format(d))
当前的方法有缺点,当检查解决方案是否为回文时,大多数生成的解决方案最终都会被丢弃 一个想法是,一旦你们从一边选择了解决方案,你们就可以立即检查最后一组中是否有相应的解决方案 例如,让我们假设您的空间是这样的
[["a","b","c"], ... , ["b","c","d"]]
我们可以看到,如果您选择“a”作为第一个选项,则最后一组中没有“a”,这排除了所有可能以其他方式尝试的解决方案。当前方法有缺点,并且当检查解决方案是否为回文时,大多数生成的解决方案最终被丢弃 一个想法是,一旦你们从一边选择了解决方案,你们就可以立即检查最后一组中是否有相应的解决方案 例如,让我们假设您的空间是这样的
[["a","b","c"], ... , ["b","c","d"]]
我们可以看到,如果您选择“a”作为第一个选择,则最后一组中没有“a”,这排除了所有可能以其他方式尝试的解决方案。对于较大的输入,您可能会通过从第一个数组中获取单词来获得一些时间增益,并将它们与最后一个数组中的单词进行比较,以检查这些对是否仍然允许形成回文,或者通过在剩余单词之间插入数组,这样的组合永远不会产生回文 通过这种方式,您可能会取消很多可能性,并且在确定一对仍在运行时,可以递归地重复此方法。然后保存两个单词的公共部分(当然,当第二个单词颠倒时),并将其余字母分开,以便在递归部分中使用 根据两个单词中的哪一个更长,您可以将剩余的字母与从左到右的下一个数组中的单词进行比较 这将在搜索树中带来大量的早期修剪。因此,您不会执行组合的完全笛卡尔积 我还编写了从给定单词中获取所有子字符串的函数,您可能已经有了:
def allsubstr(str):
return [str[i:j+1] for i in range(len(str)) for j in range(i, len(str))]
def getpalindromes_trincot(aList):
def collectLeft(common, needle, i, j):
if i > j:
return [common + needle + common[::-1]] if needle == needle[::-1] else []
results = []
for seq in aRevList[j]:
if seq.startswith(needle):
results += collectRight(common+needle, seq[len(needle):], i, j-1)
elif needle.startswith(seq):
results += collectLeft(common+seq, needle[len(seq):], i, j-1)
return results
def collectRight(common, needle, i, j):
if i > j:
return [common + needle + common[::-1]] if needle == needle[::-1] else []
results = []
for seq in aList[i]:
if seq.startswith(needle):
results += collectLeft(common+needle, seq[len(needle):], i+1, j)
elif needle.startswith(seq):
results += collectRight(common+seq, needle[len(seq):], i+1, j)
return results
aRevList = [[seq[::-1] for seq in seqs] for seqs in aList]
return collectRight('', '', 0, len(aList)-1)
# sample input and call:
input = ['already', 'days', 'every', 'year', 'later'];
aList = [allsubstr(word) for word in input]
result = getpalindromes_trincot(aList)
我与martineau发布的解决方案进行了时间比较。对于我使用的示例数据,此解决方案的速度大约快100倍:
看到它继续运行了吗
另一个优化
当第一个数组有多个具有相同字符串的条目时,不重复搜索也会带来一些好处,比如示例数据中的
'a'
。包含第二个'a'
的结果显然与第一个相同。我没有编写此优化代码,但这可能是进一步提高性能的一个想法。对于较大的输入,您可能会从第一个数组中获取单词,并将它们与最后一个数组中的单词进行比较,以检查这些对是否仍然允许形成回文,或者,这样的组合永远不会通过在剩余单词之间插入数组而产生一个
通过这种方式,您可能会取消很多可能性,并且在确定一对仍在运行时,可以递归地重复此方法。然后保存两个单词的公共部分(当然,当第二个单词颠倒时),并将其余字母分开,以便在递归部分中使用
根据两个单词中的哪一个更长,您可以将剩余的字母与从左到右的下一个数组中的单词进行比较
这将在搜索树中带来大量的早期修剪。因此,您不会执行组合的完全笛卡尔积
我还编写了从给定单词中获取所有子字符串的函数,您可能已经有了:
def allsubstr(str):
return [str[i:j+1] for i in range(len(str)) for j in range(i, len(str))]
def getpalindromes_trincot(aList):
def collectLeft(common, needle, i, j):
if i > j:
return [common + needle + common[::-1]] if needle == needle[::-1] else []
results = []
for seq in aRevList[j]:
if seq.startswith(needle):
results += collectRight(common+needle, seq[len(needle):], i, j-1)
elif needle.startswith(seq):
results += collectLeft(common+seq, needle[len(seq):], i, j-1)
return results
def collectRight(common, needle, i, j):
if i > j:
return [common + needle + common[::-1]] if needle == needle[::-1] else []
results = []
for seq in aList[i]:
if seq.startswith(needle):
results += collectLeft(common+needle, seq[len(needle):], i+1, j)
elif needle.startswith(seq):
results += collectRight(common+seq, needle[len(seq):], i+1, j)
return results
aRevList = [[seq[::-1] for seq in seqs] for seqs in aList]
return collectRight('', '', 0, len(aList)-1)
# sample input and call:
input = ['already', 'days', 'every', 'year', 'later'];
aList = [allsubstr(word) for word in input]
result = getpalindromes_trincot(aList)
我与martineau发布的解决方案进行了时间比较。对于我使用的示例数据,此解决方案的速度大约快100倍:
看到它继续运行了吗
另一个优化
当第一个数组有多个具有相同字符串的条目时,不重复搜索也会带来一些好处,比如示例数据中的
'a'
。包含第二个'a'
的结果显然与第一个相同。我没有编写此优化代码,但这可能是一个更好地提高性能的想法。是d
列表吗?换一套。你可能大部分时间都在搜索d
d是一个列表是的,我用它来避免检查一个已经计算过的字符串,如果它是回文,againIt检查一个项目是否在一个非常慢的列表中