Python中的字符串覆盖优化
Posted
技术标签:
【中文标题】Python中的字符串覆盖优化【英文标题】:String coverage optimization in Python 【发布时间】:2010-11-13 17:53:41 【问题描述】:我有这个初始字符串。
'bananaappleorangestrawberryapplepear'
还有一个带字符串的元组:
('apple', 'plepe', 'leoran', 'lemon')
我想要一个函数,以便从初始字符串和带字符串的元组中获得:
'bananaxxxxxxxxxgestrawberryxxxxxxxar'
我知道如何通过在每个单词的初始字符串中找到单词,然后在所有初始字符串中逐个字符地循环替换单词。
但它不是很有效和丑陋。我怀疑应该有一些更优雅的方法,以功能的方式,使用 itertools 或其他东西。如果您知道可以有效执行此操作的 Python 库,请告诉我。
更新:Justin Peel 指出了一个我在最初的问题中没有描述的案例。如果单词是 'aaa' 并且 'aaaaaa' 在初始字符串中,则输出应类似于 'xxxxxx'。
【问题讨论】:
【参考方案1】:import re
words = ('apple', 'plepe', 'leoran', 'lemon')
s = 'bananaappleorangestrawberryapplepear'
x = set()
for w in words:
for m in re.finditer(w, s):
i = m.start()
for j in range(i, i+len(w)):
x.add(j)
result = ''.join(('x' if i in x else s[i]) for i in range(len(s)))
print result
产生:
bananaxxxxxxxxxgestrawberryxxxxxxxar
【讨论】:
我看到的唯一问题是以下用例:其中一个词是'aaa',字符串 s = 'aaaaa'。此方法将给出 'xxxaa' 而不是 'xxxxx' 的结果,因为finditer
会找到下一个非重叠匹配。可能不会出现,但这取决于 OP 想要这个。
是的,我也不清楚单词重叠实例会发生什么。
@Justin 我没有想到那种情况,但是在字符串 'aaaaaa' 的情况下,单词 'aaa' 应该给出 'xxxxxx'。但这确实是一个极端情况,如果有更好的选择,我可以接受 'xxxaa'。
@jdnavarro:真正的问题是,对于字符串 'aaaa',单词 'aaa' 应该给出 'xxxa' 还是 'xxxx'?
@Ned @jdnavarro 我的解决方案也存在边缘情况问题。目前尚不清楚解决方案是否真的必须处理这些边缘情况。如果我更改我的解决方案来检查每个单词而不是为所有单词创建一个组合模式,那么它应该处理所有情况......或者当然在这个问题中使用编译模式的search
方法(索引为根据我的解决方案)。我想这取决于解决方案需要能够处理什么。【参考方案2】:
这是另一个答案。可能有一种更快的方法可以用 x 替换字母,但我认为没有必要,因为这已经相当快了。
import re
def do_xs(s,pats):
pat = re.compile('('+'|'.join(pats)+')')
sout = list(s)
i = 0
match = pat.search(s)
while match:
span = match.span()
sout[span[0]:span[1]] = ['x']*(span[1]-span[0])
i = span[0]+1
match = pat.search(s,i)
return ''.join(sout)
txt = 'bananaappleorangestrawberryapplepear'
pats = ('apple', 'plepe', 'leoran', 'lemon')
print do_xs(txt,pats)
基本上,我创建了一个匹配任何输入模式的正则表达式模式。然后我只是在最近匹配的起始位置之后继续从 1 开始重新开始搜索。如果您有一个输入模式是另一个输入模式的前缀,则可能会出现问题。
【讨论】:
如果您知道如何处理“xxxa”边缘情况,请告诉我您的解决方案。【参考方案3】:假设我们仅限于在没有 stdlib 和其他导入的情况下工作:
s1 = 'bananaappleorangestrawberryapplepear'
t = ('apple', 'plepe', 'leoran', 'lemon')
s2 = s1
solution = 'bananaxxxxxxxxxgestrawberryxxxxxxxar'
for word in t:
if word not in s1: continue
index = -1 # Start at -1 so our index search starts at 0
for iteration in range(s1.count(word)):
index = s1.find(word, index+1)
length = len(word)
before = s2[:index]
after = s2[index+length:]
s2 = before + 'x'*length + after
print s2 == solution
【讨论】:
好吧,内置限制不是问题的一部分,因为提到的 OP 使用了 itertools(我怀疑它无论如何都会起作用,因为我们有两个参考字符串)。哦,好吧。 你知道 stdlib 中有什么可以轻松做到这一点吗? 你也许可以用 re.否则,不。【参考方案4】:>>> string_ = 'bananaappleorangestrawberryapplepear'
>>> words = ('apple', 'plepe', 'leoran', 'lemon')
>>> xes = [(string_.find(w), len(w)) for w in words]
>>> xes
[(6, 5), (29, 5), (9, 6), (-1, 5)]
>>> for index, len_ in xes:
... if index == -1: continue
... string_ = string_.replace(string_[index:index+len_], 'x'*len_)
...
>>> string_
'bananaxxxxxxxxxgestrawberryxxxxxxxar'
>>>
当然有更有效的方法,但过早的优化是万恶之源。
【讨论】:
【参考方案5】:a = ('apple', 'plepe', 'leoran', 'lemon')
b = 'bananaappleorangestrawberryapplepear'
for fruit in a:
if a in b:
b = b.replace(fruit, numberofx's)
您现在唯一要做的就是确定要替换多少个 X。
【讨论】:
这将失败,因为它不能保证完全覆盖,例如'apple' 和 'plepe' 重叠,但不会处理第二个。【参考方案6】:def mask_words(s, words):
mask = [False] * len(s)
for word in words:
pos = 0
while True:
idx = s.find(word, pos)
if idx == -1:
break
length = len(word)
for i in xrange(idx, idx+length):
mask[i] = True
pos = idx+length
# Sanity check:
assert len(mask) == len(s)
result = []
for masked, c in zip(mask, s):
result.append('x' if masked else c)
return "".join(result)
【讨论】:
我不知道这是否是您所说的“丑陋”,但它相当快且可以理解。如果您正在处理非常大的字符串且点击次数很少,您可以通过将范围存储为掩码而不是完整数组来减少内存使用量,但这里的性能似乎是合理的。pos = idx+length
是错误的。该位置只能添加1,否则使用yyy
和yyyyy
会失败。以上是关于Python中的字符串覆盖优化的主要内容,如果未能解决你的问题,请参考以下文章