用于在包含可能子序列列表的列表中查找可能的回文字符串的算法

时间:2016-10-16 12:55:04

标签: python algorithm

我有“n”个字符串作为输入,我将可能的子序列分成如下列表

如果输入为:aa,b,aa

我创建了一个如下所示的列表(每个列表都包含字符串的子序列):

aList = [['a', 'a', 'aa'], ['b'], ['a', 'a', 'aa']]

我想在aList的列表中找到回文组合。 例如,可能的回文就是5 - aba,aba,aba,aba,aabaa

这可以通过强力算法使用以下代码实现:

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

但是当字符串的数量和字符串的长度更大时,这种方法会导致超时。

有没有更好的解决方法?

3 个答案:

答案 0 :(得分:0)

第二版

此版本使用名为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))

答案 1 :(得分:0)

当前的方法有缺点,并且当检查解决方案是否不是回文时,大多数生成的解决方案最终被丢弃。

一个想法是,一旦您从一侧选择解决方案,您可以立即检查最后一组中是否有相应的解决方案。

例如,假设你的空间是这个

[["a","b","c"], ... , ["b","c","d"]]

我们可以看到,如果你选择" a"作为第一选择,没有" a"在最后一组中,这将排除所有可能以其他方式尝试的解决方案。

答案 2 :(得分:0)

对于较大的输入,您可以通过从第一个数组中获取单词来获得一些时间增益,并将它们与最后一个数组的单词进行比较,以检查这些对仍然允许形成回文,或者这样的组合通过从中间的其余单词插入数组,永远不会导致一个。

这样你可能会取消很多可能性,一旦你确定一对仍在运行中,这个方法可以递归重复。然后,您将保存两个单词的公共部分(当第二个单词反过来时),并将剩余的字母分开以便在递归部分中使用。

根据两个单词中哪一个更长,您可以将剩余的字母与左边或右边的数组中的单词进行比较。

这应该会在搜索树中进行大量的早期修剪。因此,您不会执行组合的完整笛卡尔积。

我还编写了函数来获取给定单词中的所有子字符串,这可能是您已经拥有的:

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倍:

repl.it

上查看它

另一个优化

当第一个数组具有多个具有相同字符串的条目(如示例数据中的'a')时,也可以在不重复搜索时找到一些增益。包含第二个'a'的结果显然与第一个<div class = 'cell'> ... <div class = 'image_container'> ... <img ...> </div> </div> 相同。我没有对此优化进行编码,但可能会更好地提高性能。