功能结果随每次运行而变化

时间:2018-07-30 17:16:05

标签: python palindrome

我具有以下函数,该函数通过删除并重新排序字符来生成字符串的最长palindrome

from collections import Counter


def find_longest_palindrome(s):
    count = Counter(s)
    chars = list(set(s))
    beg, mid, end = '', '', ''

    for i in range(len(chars)):
        if count[chars[i]] % 2 != 0:
            mid = chars[i]
            count[chars[i - 1]] -= 1
        else:
            for j in range(0, int(count[chars[i]] / 2)):
                beg += chars[i]

    end = beg
    end = ''.join(list(reversed(end)))

    return beg + mid + end


out = find_longest_palindrome('aacggg')
print(out)

我通过从C ++中“翻译” this example来获得此功能

每当我运行函数时,似乎都会随机获得以下输出之一:

a
aca
agcga

在这种情况下,正确的是'agcga',因为这是输入字符串'aacggg'的最长回文。

任何人都可以建议为什么会发生这种情况,以及如何使该函数可靠地返回最长回文吗?

P.S。 C ++代码没有此问题。

1 个答案:

答案 0 :(得分:1)

您的代码取决于list(set(s))的顺序。

但是集合是无序的。

在CPython 3.4-3.7中,碰巧要获取的字符串集的具体顺序取决于字符串的哈希值,这些哈希值在启动时就明确地随机化了,因此有意义的是,每次运行都会得到不同的结果。

在C ++中看不到此原因的原因在于C ++ set类模板不是无序集合,而是排序后的集合(基于二进制搜索树,而不是哈希表),因此您每次运行总是得到相同的顺序。

您可以通过在集合上调用sorted来获得相同的Python行为,而不仅仅是按照顺序将其复制到列表中。

但是代码仍然不正确;它只是在某些示例中起作用,因为排序顺序恰好使您以最重复的顺序获得了字符。但这显然不正确,因此您需要重新考虑自己的逻辑。


翻译中最明显的区别是:

count[ch--]--;

…,或者,因为您是按索引而不是直接遍历字符,所以更像:

count[chars[i--]]--;

无论哪种方式,这都会减少当前字符的计数,然后减少当前字符,以便循环将在下一次重新检查同一字符。您已经将其变成完全不同的东西:

count[chars[i - 1]] -= 1

这只会减少前一个字符的计数。

在for-each循环中,您不能只更改循环变量并对循环产生任何影响。要完全复制C ++行为,您要么需要切换到while循环,要么将while True:循环放入for循环中以获取相同的“重复相同字符”效果。

当然,您必须减少当前字符的计数,而不是减少您再也不会看到的前一个字符的计数。

for i in range(len(chars)):
    while True:
        if count[chars[i]] % 2 != 0:
            mid = chars[i]
            count[chars[i]] -= 1
        else:
            for j in range(0, int(count[chars[i]] / 2)):
                beg += chars[i]
            break

当然,您可以显然地简化此过程-从循环for ch in chars:开始,但是如果考虑两个循环如何协同工作的逻辑,则应该能够看到如何去除整个缩进层这里。但这似乎是对代码的最小更改。


请注意,如果您进行了此更改,而没有进行sorted更改,则在正确答案模棱两可的情况下将随机选择答案-例如,您的示例将一次给出agcga,然后是{{1 }}。

添加aggga将使该选择保持一致,但同样如此。