【问题标题】:Longest Common Prefix from list elements in PythonPython中列表元素的最长公共前缀
【发布时间】:2021-12-10 16:13:24
【问题描述】:

我有一个如下列表:

strs = ["flowers", "flow", "flight"]

现在,我想从列表中找到元素的longest prefix。如果没有匹配,那么它应该返回""。我正在尝试使用'Divide and Conquer' 规则来解决问题。以下是我的代码:

strs = ["flowers", "flow", "flight"]

firstHalf = ""
secondHalf = ""


def longestCommonPrefix(strs) -> str:
    minValue = min(len(i) for i in strs)
    length = len(strs)
    middle_index = length // 2
    firstHalf = strs[:middle_index]
    secondHalf = strs[middle_index:]
    minSecondHalfValue = min(len(i) for i in secondHalf)
    matchingString=[] #Creating a stack to append the matching characters
    for i in range(minSecondHalfValue):
        secondHalf[0][i] == secondHalf[1][i]
    return secondHalf


print(longestCommonPrefix(strs))

我能够将middivide 列表分为两部分。现在我正在尝试使用后半部分并获得最长的前缀,但我无法这样做。我已经创建了一个stack,我将在其中添加连续匹配的字符,然后我将使用它与firstHalf 进行比较,但是如何比较获取continuous matching characters from start

预期输出:

"fl"

只是一个建议也会有所帮助。我可以试试看。

【问题讨论】:

标签: python python-3.x list


【解决方案1】:

无论如何,您需要依次查看每个字符串中的每个字符(直到找到一组不匹配的对应字符),因此拆分列表没有任何好处。当公共前缀不再常见时,只需迭代并中断:

def common_prefix(strs) -> str:
    prefix = ""
    for chars in zip(*strs):
        if len(set(chars)) > 1:
            break
        prefix += chars[0]
    return prefix


print(common_prefix(["flowers", "flow", "flight"]))  # fl

【讨论】:

  • 与单线解决方案相同:''.join(c[0] for c in itertools.takewhile(lambda x: len(set(x)) == 1, zip(*strs)))
  • 正如我在问题中提到的,我只想使用'Divide and Conquer' 规则解决它,因为我正在学习。你能帮我实现那个目标吗?
  • “分而治之”背后的想法通常是通过将大问题分解为更小的问题来简化它。您希望通过拆分来简化问题的哪个特定部分?
  • 这也意味着你使用了某种多线程来使其生效
  • 明白了。谢谢@Samwise
【解决方案2】:

即使这个问题已经找到了解决方案,我也想发布我的方法(我认为这个问题很有趣,所以开始玩弄它)。 因此,您的分而治之解决方案将涉及将一个非常大的任务拆分为许多较小的子任务,这些子任务的解决方案由其他小任务处理,依此类推,直到您获得最终解决方案。典型的例子是数字的总和(我们取 1 到 8),可以顺序进行(1 + 2 = 3,然后 3 + 3 = 6,然后 6 + 4 = 10...直到结束)或拆分问题(1 + 2 = 3、3 + 4 = 7、5 + 6 = 11、7 + 8 = 15,然后 3 + 7 = 10 和 11 + 15 = 26...)。第二种方法具有明显的优势,即可以并行化——在正确的设置中显着提高时间性能——这就是为什么这通常与多线程等主题密切相关。

所以我的做法:

import math

def run(lst):
    if len(lst) > 1:
        lst_split = [lst[2 * (i-1) : min(len(lst) + 1, 2 * i)] for i in range(1, math.ceil(len(lst)/2.0) + 1)]
        lst = [Processor().process(*x) for x in lst_split]
        if any([len(x) == 0 for x in lst]):
            return ''
        return run(lst)
    else:
        return lst[0]
    

class Processor:

   def process(self, w1, w2 = None):
       if w2 != None:
           zipped = list(zip(w1, w2))
           for i, (x, y) in enumerate(zipped):
               if x != y:
                   return w1[:i]
               if i + 1 == len(zipped):
                   return w1[:i+1]
       else:
           return w1
       return ''

lst = ["flowers", "flow", "flight", "flask", "flock"]

print(run(lst))

输出

fl

如果您查看run 方法,传递的lst 会分成几对,然后进行处理(这是您可以启动多个线程的地方,但我们不要专注于此)。结果列表会被重新处理直到结束。

这个问题的一个有趣的方面是:如果在 pass 之后得到一个空匹配(两个单词没有共同的开头),你可以停止 reduction ,假设您已经知道解决方案!因此引入了

if any([len(x) == 0 for x in lst]):
    return ''

我不认为functools.reduce 提供了在满足特定条件时停止迭代的可能性。

出于好奇:另一种解决方案可以利用regex

import re

pattern = re.compile("(\w+)\w* \\1\w*")

def find(x, y):
    v = pattern.findall(f'{x} {y}')
    return v[0] if len(v) else ''


reduce(find, lst)

输出

'fl'

【讨论】:

    【解决方案3】:

    有点“分而治之”:

    • 求解 2 个字符串
    • 求解其他字符串
    def common_prefix2_(s1: str, s2: str)-> str:
        if not s1 or not s2: return "" 
        for i, z in enumerate(zip(s1,s2)):
            if z[0] != z[1]:
                break
        else:
            i += 1
        return s1[:i]
    
    from functools import reduce
    def common_prefix(l:list):
        return reduce(common_prefix2_, l[1:], l[0]) if len(l) else ''
    

    测试

    for l in [["flowers", "flow", "flight"],
              ["flowers", "flow", ""],
              ["flowers", "flow"],
              ["flowers", "xxx"],
              ["flowers" ],
              []]:
        print(f"{l if l else '[]'}: '{common_prefix(l)}'")
    
    # output
    ['flowers', 'flow', 'flight']: 'fl'
    ['flowers', 'flow', '']: ''
    ['flowers', 'flow']: 'flow'
    ['flowers', 'xxx']: ''
    ['flowers']: 'flowers'
    []: ''
    

    【讨论】:

      猜你喜欢
      • 2020-12-14
      • 2020-07-05
      • 2021-09-11
      • 1970-01-01
      • 2022-11-22
      • 2021-10-12
      • 1970-01-01
      • 1970-01-01
      • 2018-05-07
      相关资源
      最近更新 更多