【问题标题】:Python - how to find all intersections of two strings?Python - 如何找到两个字符串的所有交集?
【发布时间】:2011-09-27 15:43:24
【问题描述】:

如何找到两个字符串的所有交集(也称为最长公共子串)及其在两个字符串中的位置?

例如,如果 S1="never"S2="forever" 则结果交集必须是 ["ever"] 并且它的位置是 [(1,3)]。如果S1="address"S2="oddness" 则结果交叉点为["dd","ess"],它们的位置为[(1,1),(4,4)]

最好不包含任何库的最短解决方案。但也欢迎任何正确的解决方案。

【问题讨论】:

  • 子字符串是否必须出现在两个字符串中的相同位置(就像在您的两个示例中一样)?
  • @aix:不,它们可以出现在不同的位置,例如“从不”和“永远”。我已经改变了例子。
  • 如果"call""wall" 的正确解决方案是["all"] 而不是["a", "al", "all", "ll", "l", "l"],那么“所有解决方案”是什么意思?你的意思是所有的最大解决方案吗?这里的准确定义是什么?
  • 你关心时间复杂度,还是不太关心?
  • @psihodelia:然后发布您的代码作为起点。

标签: python string algorithm


【解决方案1】:

嗯,你是说你不能包含任何库。但是,Python 的标准 difflib 包含一个完全符合您期望的函数。考虑到这是一道 Python 面试题,熟悉 difflib 可能是面试官所期望的。

In [31]: import difflib

In [32]: difflib.SequenceMatcher(None, "never", "forever").get_matching_blocks()
Out[32]: [Match(a=1, b=3, size=4), Match(a=5, b=7, size=0)]


In [33]: difflib.SequenceMatcher(None, "address", "oddness").get_matching_blocks()
Out[33]: [Match(a=1, b=1, size=2), Match(a=4, b=4, size=3), Match(a=7, b=7, size=0)]

您总是可以忽略最后一个 Match 元组,因为它是虚拟的(根据文档)。

【讨论】:

  • 谢谢!我认为你的方法是最好的。
【解决方案2】:

这可以在 O(n+m) 中完成,其中 nm 是输入字符串的长度。

伪代码为:

function LCSubstr(S[1..m], T[1..n])
    L := array(1..m, 1..n)
    z := 0
    ret := {}
    for i := 1..m
        for j := 1..n
            if S[i] = T[j]
                if i = 1 or j = 1
                    L[i,j] := 1
                else
                    L[i,j] := L[i-1,j-1] + 1
                if L[i,j] > z
                    z := L[i,j]
                    ret := {}
                if L[i,j] = z
                    ret := ret ∪ {S[i-z+1..z]}
    return ret

有关更多详细信息,请参阅Longest_common_substring_problem 维基百科文章。

【讨论】:

  • 是的,这是我的第一个想法,但是动态编程通常并不完全是单线的。
【解决方案3】:

这是我能想到的:

import itertools

def longest_common_substring(s1, s2):
   set1 = set(s1[begin:end] for (begin, end) in
              itertools.combinations(range(len(s1)+1), 2))
   set2 = set(s2[begin:end] for (begin, end) in
              itertools.combinations(range(len(s2)+1), 2))
   common = set1.intersection(set2)
   maximal = [com for com in common
              if sum((s.find(com) for s in common)) == -1 * (len(common)-1)]
   return [(s, s1.index(s), s2.index(s)) for s in maximal]

检查一些值:

>>> longest_common_substring('address', 'oddness')
[('dd', 1, 1), ('ess', 4, 4)]
>>> longest_common_substring('never', 'forever')
[('ever', 1, 3)]
>>> longest_common_substring('call', 'wall')
[('all', 1, 1)]
>>> longest_common_substring('abcd1234', '1234abcd')
[('abcd', 0, 4), ('1234', 4, 0)]

【讨论】:

  • @agf 这就是它返回的内容......还是我错过了什么?添加了一个测试,显示它返回多个值
  • 更新了我的答案,将每个子字符串的索引包含在两个字符串中。 @psihodelia 这就是你想要的吗?
  • 啊,我明白了。我错过了最大的含义。查看更新的答案。
  • 宾果游戏。想想时间复杂度让我不寒而栗,比如 O(n*(n!) + m*(m!))?
  • 是的,运行时间很糟糕,但无论如何它应该适用于小字符串。
【解决方案4】:

包括电池!

difflib 模块可能对您有所帮助 - 这是一个快速而肮脏的并行 diff:

>>> import difflib
>>> list(difflib.ndiff("never","forever"))
['- n', '+ f', '+ o', '+ r', '  e', '  v', '  e', '  r']
>>> diffs = list(difflib.ndiff("never","forever"))
>>> for d in diffs:
...   print {' ': '  ', '-':'', '+':'    '}[d[0]]+d[1:]
...
 n
     f
     o
     r
   e
   v
   e
   r

【讨论】:

    【解决方案5】:

    我假设您只希望子字符串在它们各自的字符串中具有相同的绝对位置时匹配。例如,“abcd”和“bcde”不会有任何匹配项,即使两者都包含“bcd”。

    a = "address"
    b = "oddness"
    
    #matches[x] is True if a[x] == b[x]
    matches = map(lambda x: x[0] == x[1], zip(list(a), list(b)))
    
    positions = filter(lambda x: matches[x], range(len(a)))
    substrings = filter(lambda x: x.find("_") == -1 and x != "","".join(map(lambda x: ["_", a[x]][matches[x]], range(len(a)))).split("_"))
    

    职位 = [1, 2, 4, 5, 6]

    子字符串 = ['dd', 'ess']

    如果你只想要子字符串,你可以把它压缩成一行:

    filter(lambda x: x.find("_") == -1 and x != "","".join(map(lambda x: ["_", a[x]][map(lambda x: x[0] == x[1], zip(list(a), list(b)))[x]], range(len(a)))).split("_"))
    

    【讨论】:

    • 糟糕,在我写答案时问题似乎发生了变化 - OP 确实希望“abcd”和“bcde”匹配,但我开始时并不清楚。
    【解决方案6】:
    def  IntersectStrings( first,  second):
    x = list(first)
    #print x
    y = list(second)
    lst1= []
    lst2= []
    for i in x:
        if i in y:
            lst1.append(i)
    lst2 = sorted(lst1) + []
       # This  above step is an optional if it is required to be sorted      alphabetically use this or else remove it
    return ''.join(lst2)
    
    print IntersectStrings('hello','mello' )
    

    【讨论】:

    • 输出 ello 为字符串
    猜你喜欢
    • 1970-01-01
    • 2020-08-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多