确定多个字符串的公共前缀

问题描述 投票:0回答:11

我有一组字符串,例如

my_prefix_what_ever
my_prefix_what_so_ever
my_prefix_doesnt_matter

我只是想找到这些字符串中最长的公共部分,这里是前缀。在上面的结果应该是

my_prefix_

琴弦

my_prefix_what_ever
my_prefix_what_so_ever
my_doesnt_matter

应该导致前缀

my_

Python 中是否有一种相对轻松的方法来确定前缀(无需手动遍历每个字符)?

PS:我使用的是 Python 2.6.3.

python string prefix
11个回答
165
投票

永远不要重写提供给您的内容:

os.path.commonprefix
正是这样做的:

返回最长路径前缀(取 逐个字符),这是列表中所有路径的前缀。如果列表 为空,返回空字符串 (

''
)。请注意,这可能会返回 无效的路径,因为它一次处理一个字符。

为了与其他答案进行比较,这里是代码:

# Return the longest prefix of all list elements.
def commonprefix(m):
    "Given a list of pathnames, returns the longest common leading component"
    if not m: return ''
    s1 = min(m)
    s2 = max(m)
    for i, c in enumerate(s1):
        if c != s2[i]:
            return s1[:i]
    return s1

17
投票

Ned Batchelder 可能是对的。但为了好玩,这里有一个更有效的版本 phimuemue 使用

itertools
的答案。

import itertools

strings = ['my_prefix_what_ever', 
           'my_prefix_what_so_ever', 
           'my_prefix_doesnt_matter']

def all_same(x):
    return all(x[0] == y for y in x)

char_tuples = itertools.izip(*strings)
prefix_tuples = itertools.takewhile(all_same, char_tuples)
''.join(x[0] for x in prefix_tuples)

作为对可读性的侮辱,这里有一个单行版本:)

>>> from itertools import takewhile, izip
>>> ''.join(c[0] for c in takewhile(lambda x: all(x[0] == y for y in x), izip(*strings)))
'my_prefix_'

6
投票

这是我的解决方案:

a = ["my_prefix_what_ever", "my_prefix_what_so_ever", "my_prefix_doesnt_matter"]

prefix_len = len(a[0])
for x in a[1 : ]:
    prefix_len = min(prefix_len, len(x))
    while not x.startswith(a[0][ : prefix_len]):
        prefix_len -= 1

prefix = a[0][ : prefix_len]

3
投票

以下是一个有效但可能效率很低的解决方案。

a = ["my_prefix_what_ever", "my_prefix_what_so_ever", "my_prefix_doesnt_matter"]
b = zip(*a)
c = [x[0] for x in b if x==(x[0],)*len(x)]
result = "".join(c)

对于小的字符串集合,以上完全没有问题。但是对于更大的集合,我个人会编写另一个手动解决方案,一个接一个地检查每个字符,并在存在差异时停止。

从算法上讲,这会产生相同的过程,但是,可以避免构建列表

c
.


1
投票

出于好奇,我想出了另一种方法:

def common_prefix(strings):

    if len(strings) == 1:#rule out trivial case
        return strings[0]

    prefix = strings[0]

    for string in strings[1:]:
        while string[:len(prefix)] != prefix and prefix:
            prefix = prefix[:len(prefix)-1]
        if not prefix:
            break

    return prefix

strings = ["my_prefix_what_ever","my_prefix_what_so_ever","my_prefix_doesnt_matter"]

print common_prefix(strings)
#Prints "my_prefix_"

正如 Ned 指出的那样,使用

os.path.commonprefix
可能更好,这是一个非常优雅的功能。


1
投票

第二行对输入字符串中的每个字符使用 reduce 函数。它返回一个包含 N+1 个元素的列表,其中 N 是最短输入字符串的长度。

lot 中的每个元素是 (a) 输入字符,如果 all 输入字符串在该位置匹配,或者 (b) 无。 lot.index(None)是第一个None在lot中的位置:公共前缀的长度。 out 是通用前缀。

val = ["axc", "abc", "abc"]
lot = [reduce(lambda a, b: a if a == b else None, x) for x in zip(*val)] + [None]
out = val[0][:lot.index(None)]

1
投票

这是一个简单干净的解决方案。这个想法是使用 zip() 函数通过将所有字符放入第一个字符列表、第二个字符列表、...第 n 个字符列表中来排列所有字符。然后迭代每个列表以检查它们是否仅包含 1 个值。

a = ["my_prefix_what_ever", "my_prefix_what_so_ever", "my_prefix_doesnt_matter"]

list = [all(x[i] == x[i+1] for i in range(len(x)-1)) for x in zip(*a)]

print a[0][:list.index(0) if list.count(0) > 0 else len(list)]

输出:my_prefix_


0
投票

这是使用 OrderedDict 和最少代码执行此操作的另一种方法。

import collections
import itertools

def commonprefix(instrings):
    """ Common prefix of a list of input strings using OrderedDict """

    d = collections.OrderedDict()

    for instring in instrings:
        for idx,char in enumerate(instring):
            # Make sure index is added into key
            d[(char, idx)] = d.get((char,idx), 0) + 1

    # Return prefix of keys while value == length(instrings)
    return ''.join([k[0] for k in itertools.takewhile(lambda x: d[x] == len(instrings), d)])

0
投票

我的问题略有不同,谷歌将我发送到这里,所以我认为记录它会很有用:

我有一个列表:

  • my_prefix_what_ever
  • my_prefix_what_so_ever
  • my_prefix_doesnt_matter
  • 一些噪音
  • some_other_noise

所以我希望

my_prefix
被退回。这可以通过以下方式完成:

from collections import Counter

def get_longest_common_prefix(values, min_length):
    substrings = [value[0: i-1] for value in values for i in range(min_length, len(value))]
    counter = Counter(substrings)
    # remove count of 1
    counter -= Counter(set(substrings))
    return max(counter, key=len)

0
投票

在一行中不使用 itertools,没有特别的原因,尽管它确实遍历了每个字符:

''.join([z[0] for z in zip(*(list(s) for s in strings)) if all(x==z[0] for x in z)])

0
投票

从给定的输入字符串中找出所有词的共同前缀,如果没有共同前缀打印-1

stringList = ['my_prefix_what_ever', 'my_prefix_what_so_ever', 'my_prefix_doesnt_matter']
len2 = len( stringList )
if len2 != 0:
    # let shortest word is prefix
    prefix = min( stringList )
    for i in range( len2 ):
        word = stringList[ i ]
        len1 = len( prefix )
        # slicing each word as lenght of prefix
        word = word[ 0:len1 ]
        for j in range( len1 ):
            # comparing each letter of word and prefix
            if word[ j ] != prefix[ j ]:
                # if letter does not match slice the prefix
                prefix = prefix[ :j ]
                break # after getting comman prefix move to next word
    if len( prefix ) != 0:
        print("common prefix: ",prefix)
    else:
        print("-1")
else:
     print("string List is empty") 
© www.soinside.com 2019 - 2024. All rights reserved.