Python 从一组(类似)字符串中确定前缀

Python 从一组(类似)字符串中确定前缀,python,string,prefix,Python,String,Prefix,我有一套字符串,例如 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:我正在使用P

我有一套字符串,例如

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:我正在使用Python2.6.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

以下是我的解决方案:

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]

永远不要重写提供给您的内容:是否确实:

返回最长路径前缀(采用 一个字符接一个字符),它是列表中所有路径的前缀。如果列表 如果为空,则返回空字符串(
'
)。请注意,这可能会返回 无效路径,因为它一次只能处理一个字符

为了与其他答案进行比较,代码如下:

# 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
这可能是对的。但为了好玩,这里有一个更有效的版本,使用
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)
作为对可读性的冒犯,这里有一个单行版本:)


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

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
,这是一个非常优雅的函数。

下面是另一种使用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)])

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

批次中的每个元素要么是(a)输入字符,如果所有输入字符串在该位置匹配,要么是(b)无批次。索引(None)是批次中第一个None的位置:公共前缀的长度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)]

这里有一个简单的清洁解决方案。其思想是使用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)]

输出:我的_前缀

我的问题略有不同,google将我发送到这里,因此我认为记录以下内容会很有用:

我有这样一个清单:

  • 我的前缀是什么
  • 我的前缀是什么
  • 我的前缀无关紧要
  • 一些噪音
  • 一些其他的噪音
因此,我希望
我的前缀
会被返回。这可以通过以下方式实现:

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)

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

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

那么您实际上是在请求?欢迎使用堆栈溢出!虽然这段代码片段可能会解决这个问题,包括如何以及为什么解决这个问题的解释,以提高您的文章的质量。记住,你是在将来回答读者的问题,而不仅仅是现在提问的人!请在回答中添加解释,并说明适用的限制和假设。这是如何清洁的?它是如何不清洁的?其他解决方案具有块中的代码。逻辑很简单,可以在一个赋值中完成。我认为这只能处理m中的两个字符串,不是吗?不过评论说“所有列表元素,有点像是表示任意数量的元素”@sramij不完全是这样!字符串上的min()和max()是字典中的最小值和最大值。所以当最小值和最大值有相同的第一个字母时,它们之间的所有其他单词也必须有相同的字母,依此类推。参数是否需要是有效的路径名?如果不是,会发生什么?文档中什么也没说,所以我不确定这是否可以用于任意字符串。@hochl没有。这段代码只是查看字符串,而不是路径。如果它们恰好是所有路径,请注意此前缀
commonprefix({“/aaA/b”,“/aaB/b”})=“/aa”
,这可能不是您要使用的路径。@hochi如果您确实需要有效路径,请查看姐妹函数。从文档中可以看到:“与commonprefix()不同,它返回一个有效路径。”对于Python3,请将
itertools.izip(*strings)
替换为
zip(*strings)
''.join([z[0] for z in zip(*(list(s) for s in strings)) if all(x==z[0] for x in z)])