我一直在尝试使用表格方法来简化python中的布尔表达式。为此,我需要检查两个给定的字符串是否只在一个索引上有所不同 例如,对于以下示例,该函数应返回以下内容:
0011
和0111
- true,因为两者仅在第1位有所不同0-001
和0-101
- 仅在2 0-011
和0-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中这样做?
答案 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并删除了长度测试。
使用更长的字符串进行测试:
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