我具有以下函数,该函数通过删除并重新排序字符来生成字符串的最长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 ++代码没有此问题。
答案 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
将使该选择保持一致,但同样如此。