问题条件是:
文本文件包含一串字符,其中可能包括拉丁字母 A...Z 的大写字母。求恰好包含三个 Z(不一定是相邻的)的最长子串的长度。
我想到了使用 re 库来解决这个问题:
import re
with open("text.txt") as file:
string = file.read()
substrings = re.findall(r"[^Z]*Z" * 3 + r"[^Z]*", string)
print(len(max(substrings, key=len)))
但答案不匹配:正确的是338,代码显示297。你能告诉我错误是什么吗?
正则表达式引擎不会返回重叠的匹配项。也就是说,并不是所有搜索到的子串都会被扫描,而是每四个子串都会被扫描一次。没有简单的方法可以解决这种情况。
1
解决方案没有
re
.我们读取文件,将其拆分为不带“Z”的单词,返回这些单词的长度。我们根据长度建立累积和sums
。使用累积和列表,让我们使用两个间隔为四个位置的迭代器。迭代器之间的差异是四个连续单词的长度之和。我们选择最大值,添加三个以纪念四个单词之间删除的三个分隔符:如果你不介意再次复制数据,最后四行可以替换为一行:
2
可以制定无需线性附加内存的解决方案。
chars(f)
打印文件中的连续字符。zs(seq)
返回序列中“Z”字符的位置seq
。此外,它还给出了序列的开始 ( -1itertools.tee
) 和结束位置,就好像它被“Z”字符包围一样。接受一个迭代器并制作它的两个副本。在底层,它会记住原始迭代器的值。在我们的问题中,他会记住四个整数——常量记忆。两个迭代器沿着位置序列运行,间隔为四个位置。迭代器之间的差异是不带“Z”的四个连续单词的总长度。我们选择最大值,减去一:行尾有一个额外的字符“Z”。
这是一个恒定内存的解决方案,您可以处理任何大小的文件:
PS在第一个程序中,该块
for
被删除,with
因为正在内存中创建文件的副本。在第二个程序中,该块必须保留在内部。itertools.tee
出于教育目的,可以将PPSinterval(seq, k)
替换为一个函数,该函数接受seq
并返回彼此相距一定距离的值对k
。那么计算逻辑就变成了一行:文件中的字符→'Z'位置→位置间隔→最大间隔长度:准备好用于测试的控制台脚本(该行
AZBZCZDZEEE
输出9):由于生成器的轻微复杂性和队列的内存消耗,队列的大小不会超过所需 Z 的数量(本例中为 3 个)加 1,因此我们获得了现成的链长度,我们需要从中选择最大的一个。
如果在最后一行我们将“(c for c in data)”更改为 Stanislav 的 chars(f) 函数,那么一切都应该正常。
PS 如果需要,您可以轻松获取链的起始和结束索引。
总的来说,我明白发生了什么:findall函数不会一次找到所有子字符串,而是首先查找、记录并且不再考虑,这就是为什么它得到错误答案的原因。
如果有人能想到这一点,我将不胜感激。在我看来,这个方法并不能解决这个问题。