查找“随机”字符串列表中至少 2 个元素上存在的最长前缀 [关闭]
Posted
技术标签:
【中文标题】查找“随机”字符串列表中至少 2 个元素上存在的最长前缀 [关闭]【英文标题】:Finding longest prefix present on at least 2 elements on a list of "random" strings [closed] 【发布时间】:2017-11-02 18:17:55 【问题描述】:给定一个字符串列表,例如:
myList = ["foo", "foobar", "football", "footbag", "bar"]
找到列表中至少 2 个字符串中存在的最长前缀:
Longest prefix is "footba" present in "football" and "footbag"
列表将通过输入来填充,并且并非所有输入都有共同的前缀。
要被视为一个选项,前缀出现在列表中的两个字符串上就足够了。如果有多个选项,它必须返回最长的一个。
在我的研究中,我已经能够找到如何获得所有字符串的最长公共前缀,例如:
列表:["foo_a","foo_b","foo_c","fnord"]
输出:Longest common prefix is "f"
但是,我列表中的字符串甚至可能不以相同的字母开头。
【问题讨论】:
显示您拥有的代码。这使得这个问题更具体,更容易回答。 我的代码将是我使用输入来填充列表的函数,我相信这不会很相关,因为我包含了一个示例列表。我还没有任何代码来解决这个问题,但是根据我得到的答案,我实际上正在做一些事情。我会尽快更新的 您可以使用sliding window
来构建prefix tries
。
【参考方案1】:
您可以构建prefix tries 的森林,然后搜索具有两个(非空)子节点的最深节点的“高度”(距根的距离有多远)。这个节点代表最长的公共前缀。
如果您不关心性能,您可以简单地迭代列表中的所有单词并将它们中的每一个(其前缀)与其余单词进行比较,同时不断更新最大值:
def common_prefix_size(s1, s2):
res, i = 0, 0
while i < min(len(s1), len(s2)):
if s1[i] == s2[i]:
res += 1
i += 1
else:
break
return res
def longest_prefix(lst):
res = ''
maxsize = 0
for i in range(len(lst) - 1):
for j in range(i + 1, len(lst)):
t = common_prefix_size(lst[i], lst[j])
maxsize = max(maxsize, t)
if maxsize == t:
res = lst[i][:maxsize]
return res
myList = ["foo", "foobar", "football", "footbag", "bar"]
print(longest_prefix(myList)) # footba
【讨论】:
我一定会看看这个。根据我们在课堂上看到的情况,这似乎比我预期的要复杂一些。但我相信我可以设法理解如何做到这一点,我会从中受益。谢谢提示 @oScarDiAnno 如果您正在寻找一个不太复杂的解决方案,您可以使用简单的嵌套两个 for 循环“蛮力”摆脱它(它将每个项目与所有其他项目进行比较并更新最大限度)。我会更新答案 我正试图做这样的事情!我知道有一种方法可以用“我已经拥有的知识”来做到这一点,我也有同样的想法,但很难让它正常工作。我看到你做了什么,这对我来说更容易理解。非常感谢:) 我明白程序为什么能工作,但请告诉我变量名res
和 t
代表什么?只是想知道,也许这应该是我可以从常识中推断出来的东西,但也许是因为英语不是我的母语。再次感谢@alfasin
@oScarDiAnno 我的错:maxsize
是最大前缀的大小,res
包含最大前缀(代码已更正)。每次迭代我们检查两个字符串的common_prefix_size()
并将其保存到t
(temp)。如果t > maxsize
我们更新maxsize
并将前缀保存到res
【参考方案2】:
这是一个混乱的实现,对于大型列表来说效率不高,但它可以完成工作。我建议研究一下前面提到的前缀尝试,但如果你有一点时间,它们会工作得更好。
这从完整大小的单词向后工作,直到两个单词共享相同的前缀。它截断单词的结尾并计算同一个单词出现的次数,如果至少是 2 次,则返回它。
from collections import defaultdict
def list_to_text(x):
x = list(map(str, x))
first = '", "'.join(x[:-1]) #Join together (a, b, c)
if first:
return '" and "'.join((first, x[-1])) #Add the last element (a, b, c and d)
return x[0] #Return a single value if list length is 1
def find_longest_prefix(x):
x_max = len(max(x, key=len))
for i in range(1, x_max)[::-1]:
#Chop off the end of every word
trim = [j[:i] for j in x]
#Iterate through every unique value
result = defaultdict(list)
for j in set(trim):
result[trim.count(j)].append(j)
#Finish iterating if there are more than 2 words that share a prefix
highest_count = max(result)
if highest_count >= 2:
prefix = result[highest_count]
words = [j for k in prefix for j in x if j.startswith(k)]
return prefix, words
myList = ["foo", "foobar", "football", "footbag", "bar"]
prefix, words = find_longest_prefix(myList)
#Put together the string
print('The longest common prefix "" present in "".'.format(' is' if len(prefix) ==1 else 'es are',
list_to_text(prefix), list_to_text(words)))
它会根据结果的数量稍微格式化字符串。您的列表仍然会产生这样的结果:
The longest common prefix is "footba" present in "football" and "footbag".
但是添加另一个具有相同长度和结果数量的前缀将导致如下结果:
The longest common prefixes are "footba" and "testin" present in "football", "footbag", "testing" and "testin".
【讨论】:
令人印象深刻。我仍然意识到整个代码是如何工作的,因为我看到了一些我以前从未见过的命令,但我想在提出问题之前研究其中的一些。我必须在 print 函数中添加括号才能使其正常工作,但是我已经建议对您的答案进行编辑。 啊,我猜你是在 Python 3 上然后哈哈。如果您无法理解某些行的工作原理,我很乐意解释,尽管我猜是切片和key=len
部分。我想说了解切片的工作原理(以及列表理解,如果你没有涵盖)是至关重要的,其他部分你通常可以通过谷歌很容易找到:)
是的哈哈(我已经包含了我在我的问题中使用的 Python 版本,但它被编辑了 ¬¬)。另外,我的问题被标记为“太宽泛”。老实说,我不知道为什么,因为我包含了一个示例和所需的输出。
我明白key=len。可能会检查一下切片。但是,我仍然想知道:count = trim.count(j): j for j in set(trim)
。在trim.count(j)
,我在那里使用函数吗?我想我有点困惑,因为通常我们使用函数通过它们的“名称”和我们想要在括号内应用它的变量来调用它们。但是,我们之前使用 trim 和 count 作为变量,我自己解释一下吗?
trim.count(j)
部分计算该值在列表中出现的次数(并且与我设置的 count
变量完全分开,抱歉,哈哈),如果它出现两次,那就是你的前缀.然而,我刚刚意识到它只支持一个结果,所以我会尽快修复它:)以上是关于查找“随机”字符串列表中至少 2 个元素上存在的最长前缀 [关闭]的主要内容,如果未能解决你的问题,请参考以下文章