比较字符串,允许一个字符差异

时间:2014-08-09 07:34:09

标签: python string

我一直在尝试使用表格方法来简化python中的布尔表达式。为此,我需要检查两个给定的字符串是否只在一个索引上有所不同 例如,对于以下示例,该函数应返回以下内容:

  • 00110111 - true,因为两者仅在第1位有所不同
  • 0-0010-101 - 仅在2
  • 时为true
  • 0-0110-101 - false,差异为2,3

现在我正在使用以下功能:

def match(s1,s2):

    l=[False,-1]##returns false when they cant be combined
    for i in range(len(s1)):
        if s1[:i]==s2[:i] and s1[i]!=s2[i] and s1[i+1:]==s2[i+1:]:
            l= [True,i]
            break
    return l

我想以非常快的方式实现它(低复杂性)。有没有办法在python中这样做?

5 个答案:

答案 0 :(得分:5)

我使用Levenshtein距离匹配不同的字符串,只添加或删除一个字符(而不是简单地替换)。所以:

  • 约翰的狗
  • 约翰斯狗

被视为匹配。

Levenshtein距离是将一个字符串替换为另一个字符串所需的编辑次数。上面两个字符串的Levenshtein距离是1,因为唯一要做的就是删除一个字符。

要使用它,您只需安装相应的Levenshtein python模块:

pip install python-Levenshtein

然后使用它:

from Levenshtein import distance 
def match(s1, s2):
    return distance(s1, s2) <= 1

答案 1 :(得分:3)

这是一个性能更好的解决方案,用Python 3编写:

def match(s1, s2):
    ok = False

    for c1, c2 in zip(s1, s2):
        if c1 != c2:
            if ok:
                return False
            else:
                ok = True

    return ok

我没有检查长度差异,因为你说两个字符串相等,但是对于更通用的方法,我会添加它。

如果你需要不同角色的位置:

def match(s1, s2):
    pos = -1

    for i, (c1, c2) in enumerate(zip(s1, s2)):
        if c1 != c2:
            if pos != -1:
                return -1
            else:
                pos = i

    return pos

这些是使用timeit执行的基准测试,使用匹配进行测试(&#34; 0-001&#34;,&#34; 0-101&#34;)。我将所有解决方案翻译成py3并删除了长度测试。

  1. 您的解决方案:5.12
  2. Martijn Pieters&#39;解决方案:4.92
  3. enrico.bacis&#39;和湖泊的解决方案:5.51
  4. 我的解决方案:2.42
  5. 使用更长的字符串进行测试:

    Martijn Pieters&#39;溶液:

    timeit.timeit('match("0-0016ub5j2oi06u30tj30g6790v3nug[hoyj39867i6gy9thvb05y4b896y3n098vty98thn98qg5y4n8ygnqp", "0-0016ub5j2oi06u30tj30g6790v3gug[hoyj39867i6gy9thvb05y4b896y3n098vty98thn98qg5y4n8ygnqp")', setup="""
    def match(s1, s2):
        combo = zip(s1, s2)
        return any(c1 != c2 for c1, c2 in combo) and all(c1 == c2 for c1, c2 in combo)
    """)
    

    结果:32.82

    我的解决方案:

    timeit.timeit('match("0-0016ub5j2oi06u30tj30g6790v3nug[hoyj39867i6gy9thvb05y4b896y3n098vty98thn98qg5y4n8ygnqp", "0-0016ub5j2oi06u30tj30g6790v3gug[hoyj39867i6gy9thvb05y4b896y3n098vty98thn98qg5y4n8ygnqp")', setup="""
    def match(s1, s2):
        ok = False
    
        for c1, c2 in zip(s1, s2):
            if c1 != c2:
                if ok:
                    return False
                else:
                    ok = True
    
        return ok
    """)
    

    结果:20.21

答案 2 :(得分:2)

在Python 2中,使用future_builtins.zip()(Python 2和3兼容zip() - as-iterator)(在Python 3中,内置函数可以正常工作)将两个字符串逐个字符组合,然后使用any()all()遍历生成的迭代器:

try:
    from future_builtins import zip
except ImportError:
    pass

def match(s1, s2):
    if len(s1) != len(s2):
        return False
    combo = zip(s1, s2)
    return any(c1 != c2 for c1, c2 in combo) and all(c1 == c2 for c1, c2 in combo)

这是有效的,因为combo迭代器;它会根据需要逐个生成字符对,而any()all()只需要根据需要使用多对来确定其结果。

any()一发现两个相等的字符就会停止迭代。如果所有剩余的字符相等,all()将仅返回True。

如果恰好有一对不同,则两个条件一起为真。

因为使用了迭代器方法,所以上面确定了你的字符串是否匹配的绝对最小工作量;发现第二对与迭代停止不匹配的那一刻;看其余的角色组合是没有意义的。

演示(Python 2,导入zip()):

>>> from future_builtins import zip
>>> def match(s1, s2):
...     if len(s1) != len(s2):
...         return False
...     combo = zip(s1, s2)
...     return any(c1 != c2 for c1, c2 in combo) and all(c1 == c2 for c1, c2 in combo)
... 
>>> match('0011', '0111')
True
>>> match('0-001', '0-101')
True
>>> match('0-011', '0-101')
False

答案 3 :(得分:2)

def match(a,b):
    s = sum([a[i] != b[i] for i in range(len(a))])
    if s == 1:
       return True
    else:
       return False

答案 4 :(得分:0)

相同长度

如果两个字符串具有相同的长度:

def match(s1, s2):
    return sum(s1[i] != s2[i] for i in xrange(len(s1))) <= 1

您还可以创建这样的匹配器生成器:

def create_matcher(maxdiff):
    return lambda s1, s2: sum(s1[i] != s2[i] for i in xrange(len(s1))) <= maxdiff

match = create_matcher(1)

示例:

print match('0011', '0111')      # True
print match('0-001', '0-101')    # True
print match('0-011', '0-101')    # False

不同长度

如果它们的长度不同,我们可以假设超出的字符不同,所以:

def match(s1, s2):
    l = min(len(s1), len(s2))
    return sum(s1[i] != s2[i] for i in xrange(l)) + abs(len(s1) - len(s2)) <= 1