作者:一腕儿本人 | 来源:互联网 | 2023-02-05 13:24
1> bunji..:
这是一种通过三个步骤解决此问题的方法:
识别受替换影响的子串
从这些子字符串中的char_before
和char_after
字典中删除计数
进行替换并将新计数添加到受影响的子字符串的字典char_before
和char_after
字典中
首先让我们从定义一些变量并运行初始代码开始.
source, target = ('a', 'b'), 'x'
n = 3
char_before= defaultdict(Counter)
char_after = defaultdict(Counter)
for window in per_window(s, n):
char_after[window[:2]][window[2]] += 1
char_before[window[1:]][window[0]] += 1
现在我们找到将被替换的子串的跨度(开始和结束索引)(注意我们实际上还没有进行任何替换)
import re
spans = [m.span() for m in re.finditer(''.join(source), s)]
但是我们知道落入其中一个跨度的窗口的前后计数并不是唯一会受到替换影响的窗口.直接位于其中一个跨度之前或之后的任何窗口也将受到影响.例如,s = 'cdababef'
如果我们'ab'
用'x'
初始替换'cd'
将需要char_after
更新计数,即使'cd'
它自身的任何部分都没有被替换.
为了处理这个问题,我们定义了一个函数merge_spans
,它不仅可以合并相邻的跨度((2,4)
并且(4,6)
变为(2,6)
),还可以合并彼此extra
间隔的跨距(其中extra
是由关键字参数定义的整数).这背后的直觉是,这将返回一个跨度列表,其中跨度对应于计数在被替换影响之前/之后的所有子串.
def merge_spans(spans, extra = 0):
extra = max(0,extra)
merged = spans[:]
if len(merged) == 1:
return [(max(merged[0][0]-extra, 0), merged[0][-1]+extra)]
for i in range(1, len(merged)):
span = merged[i]
prev = merged[i-1]
if prev[-1]+extra >= span[0]-extra:
merged[i] = (max(0,prev[0]-extra), span[-1]+extra)
merged[i-1] = ()
elif i == len(merged)-1:
merged[i] = (max(0,span[0]-extra), span[-1]+extra)
merged[i-1] = (max(0,prev[0]-extra), prev[-1]+extra)
else:
merged[i-1] = (max(0,prev[0]-extra), prev[-1]+extra)
return list(filter(None, merged))
所以让我们创建这个跨度列表.我们设定extra
,n-1
因为n-1
替换品任何一方的信件都会受到影响.
merged = merge_spans(spans, n-1)
现在我们可以迭代这些跨度并删除受替换影响的窗口的计数.然后我们可以在该范围内进行替换并更新计数.
for span in merged:
sub_s = s[span[0]:span[-1]]
for window in per_window(sub_s, n):
char_after[window[:2]][window[2]] -= 1
char_before[window[1:]][window[0]] -= 1
new_s = sub_s.replace(''.join(source), target)
for window in per_window(new_s, n):
char_after[window[:2]][window[2]] += 1
char_before[window[1:]][window[0]] += 1
请注意,上述内容会影响原始字典char_before
和char_after
字典,但如果由于某种原因需要保留原始计数,则可以先复制它们.
最后,我们从计数器中删除任何计数0
或负数,并完全删除任何不包含正计数的窗口.请注意,添加Counter()
到计数器会删除任何值为正值的元素.
char_before2 = {k:v+Counter() for k,v in char_before.items() if any(v.values())}
char_after2 = {k:v+Counter() for k,v in char_after.items() if any(v.values())}
结果如下:
>>> char_before2
{('d', 'x'): Counter({'c': 1}),
('e', 'f'): Counter({'x': 1}),
('x', 'e'): Counter({'x': 1}),
('x', 'x'): Counter({'d': 1})}
>>> char_after2
{('c', 'd'): Counter({'x': 1}),
('d', 'x'): Counter({'x': 1}),
('x', 'e'): Counter({'f': 1}),
('x', 'x'): Counter({'e': 1})}