【问题标题】:Finding an Insertion in a String在字符串中查找插入
【发布时间】:2011-08-02 20:47:06
【问题描述】:

检查 StringA = StringB 是否在任意点插入另一个 StringC 的最佳方法是什么?

例如,给定abcdefabcXYZdef,我想发现abcXYZdefabcdef,而XYZ 插入在位置4。

另一方面,给定abcdefabRSTcdXYZef,我想发现第一个字符串不能通过单次插入变成第二个。

我知道我可以从两端逐个字符地检查 StringA,并检查它是否覆盖了整个 StringB,但这样写起来会相当乏味。在 Python(我正在使用)中执行此操作也会相当慢,我宁愿不为此编写特殊的 C 扩展。

我可以用 Regex 或其他标准字符串操作函数来做一些聪明的事情吗?

编辑:澄清一下,StringC 是完全未知的;甚至可能没有有效的 StringC,我想知道是不是这样。

【问题讨论】:

  • 如果您使示例字符串更短且更易于理解,这可能会有所帮助。
  • 你真的认为写起来会那么乏味吗? Python 有很好的切片工具来检查子字符串s1[:n]==s2[:n]。它当然效率不高,但我认为编写它不会花费很长时间。
  • 我不知道你为什么拒绝逐个字符的解决方案。它看起来不会超过几行代码,而且它的速度与纯 Python 差不多。
  • @mark: 主要是因为我要处理大小为 100kb 的文本字符串;我想要比纯 python =D 更快的东西。
  • 如果您需要更快的速度,逐字符比较的 C/C++ 实现可能会非常快。但首先看看下面我的 Python 实现,看看它是否足够快。

标签: python regex string algorithm


【解决方案1】:

标准库中一个被低估的宝石是difflib...

>>> import difflib
>>> s = difflib.SequenceMatcher(None, "GHSKWITNIFSI", "GHSKWAGDITNIFSI")
>>> s.get_matching_blocks()[:-1]
[(0, 0, 5), (5, 8, 7)]
>>> s = difflib.SequenceMatcher(None, "GHSKWITNIFSI", "GHSKWITNIFSI")
>>> s.get_matching_blocks()[:-1]
[(0, 0, 12)]

【讨论】:

  • +1 用于使 difflib 为人所知,但解释如何解释结果会有所帮助
  • @neurino — 每个元组代表一个匹配块;第一个数字是第一个序列中的起始偏移量,第二个是第二个序列中的起始偏移量,最后一个是匹配块的长度。
  • 不错!从来不知道那个图书馆
  • 哇...确实包括电池!
【解决方案2】:

这...在某种程度上感觉很笨拙,而且可能只是到了一半,但它似乎在您的示例中找到了子字符串,并且可能会扩展一点。我可以在一分钟内对其进行修改,并有更多时间进行测试,但这是一个方法概念:

s1 = 'GHSKWITNIFSI'
s2 = 'GHSKWAGDITNIFSI'

l = len(s2) - len(s1)

for i in range(len(s1)):
 if s2[0:i] + s2[i + l:] == s1:
  print i
  break

我不喜欢使用range(len()),但在这个特定的使用场景中我认为它是合适的。如果单次插入将 s1 变为 s2,它将打印发生插入的索引。

【讨论】:

  • 你为什么不喜欢 range(len())?
  • @krs1 - 它通常不是“pythonic”......您通常直接迭代一个可迭代对象,或者如果您必须具有索引值,则使用 enumerate(iterable) 使它们可用。就像我说的那样,在这种情况下它可能是合适的。
【解决方案3】:

我不知道,但您正在尝试查找“编辑距离”。查看维基百科:

http://en.wikipedia.org/wiki/Edit_distance

您还可以查看 Peter Norvig 的拼写校正器:

http://norvig.com/spell-correct.html

我认为您可以调整拼写校正器中的代码来满足您的需要。

祝你好运。

【讨论】:

  • 也是“最长公共子串”
【解决方案4】:
def GetInsertedString(StringA, StringB):
    lenA = len(StringA)
    lenB = len(StringB)
    if lenA > lenB:
        return None, None
    begincount = 0
    while begincount < lenA and StringA[begincount] == StringB[begincount]:
        begincount += 1
    endcount = 0
    while endcount < (lenA - begincount) and StringA[lenA-endcount-1] == StringB[lenB-endcount-1]:
        endcount += 1
    if begincount + endcount != lenA:
        return None, None
    return begincount, StringB[begincount:begincount+lenB-lenA]

>>> GetInsertedString('GHSKWITNIFSI', 'GHSKWAGDITNIFSI')
(5, 'AGD')
>>> GetInsertedString('GHSKWITNIFSI', 'GHSKWAGDTNIFSI')
(None, None)

【讨论】:

    【解决方案5】:
    from itertools import dropwhile
    
    def get_inserted_substring(s1, s2):
        try:
            # diff is the first index at which the strings differ
            diff = dropwhile(lambda i: s1[i] == s2[i], xrange(len(s2))).next()
            if s2[diff:].endswith(s1[diff:]):
                return (diff, s2[diff:diff-len(s1)])
        except (StopIteration, IndexError):
            # the strings are the same or only differ at the end
            if len(s1) <= len(s2):
                return (len(s1), s2[len(s1):])
        return (None, None)
    

    还有例子……

    >>> get_inserted_substring('abcdef', 'abcXYZdef')
    (3, 'XYZ')
    >>> get_inserted_substring('abcdef', 'abRSTcdXYZef')
    (None, None)
    >>> get_inserted_substring('abcdef', 'abcdefXYZ')
    (6, 'XYZ')
    >>> get_inserted_substring('abcdef', 'XYZabcdef')
    (0, 'XYZ')
    >>> get_inserted_substring('abcdefXYZ', 'abcdef')
    (None, None)
    

    【讨论】:

      【解决方案6】:
      strA='foor'
      strB='foobar'
      strC='ba'
      
      if strB.replace(strC,'') == strA:
          print strC,' at index ',len(strB.split(strC)[0])
      

      可能吗?正在测试中...

      【讨论】:

      • 我不认为 strC 是一个已知值 - 这就是他试图确定的。如果知道,就没有理由提出这个问题。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-11
      • 1970-01-01
      • 2023-03-09
      • 1970-01-01
      相关资源
      最近更新 更多