假设我有一个字符串
s = 'AAABBBCAB'
如何高效地计算字符串中每个字符出现次数的前缀和,即:
psum = [{'A': 1}, {'A': 2}, {'A': 3}, {'A': 3, 'B': 1}, {'A': 3, 'B': 2}, {'A': 3, 'B': 3}, {'A': 3, 'B': 3, 'C': 1}, {'A': 4, 'B': 3, 'C': 1}, {'A': 4, 'B': 4, 'C': 1}]
假设我有一个字符串
s = 'AAABBBCAB'
如何高效地计算字符串中每个字符出现次数的前缀和,即:
psum = [{'A': 1}, {'A': 2}, {'A': 3}, {'A': 3, 'B': 1}, {'A': 3, 'B': 2}, {'A': 3, 'B': 3}, {'A': 3, 'B': 3, 'C': 1}, {'A': 4, 'B': 3, 'C': 1}, {'A': 4, 'B': 4, 'C': 1}]
itertools.accumulate
和 collections.Counter
在一行代码中完成此操作:from collections import Counter
from itertools import accumulate
s = 'AAABBBCAB'
psum = list(accumulate(map(Counter, s)))
这会给你一个Counter
对象的列表。现在,要在O(1)时间内获取s
的任何子字符串的频率,你只需相减计数器,例如:
>>> psum[6] - psum[1] # get frequencies for s[2:7]
Counter({'B': 3, 'A': 1, 'C': 1})
这是一个选项:
from collections import Counter
c = Counter()
s = 'AAABBBCAB'
psum = []
for char in s:
c.update(char)
psum.append(dict(c))
# [{'A': 1}, {'A': 2}, {'A': 3}, {'A': 3, 'B': 1}, {'A': 3, 'B': 2},
# {'A': 3, 'B': 3}, {'A': 3, 'B': 3, 'C': 1}, {'A': 4, 'B': 3, 'C': 1},
# {'A': 4, 'B': 4, 'C': 1}]
collections.Counter
来保持“累加和”,并将(结果的副本)添加到列表psum
中。这样,我只需要对字符串s
进行一次迭代。collections.Counter
对象,你可以将最后一行改为:psum.append(c.copy())
[Counter({'A': 1}), Counter({'A': 2}), ...
Counter({'A': 4, 'B': 4, 'C': 1})]
使用这个方法也可以达到同样的效果(accumulate
的使用最初是在Eugene Yarmash的答案中提出的;我只是避免使用map
而选择了生成器表达式):
from itertools import accumulate
from collections import Counter
s = "AAABBBCAB"
psum = list(accumulate(Counter(char) for char in s))
为了完整起见(因为这里还没有“纯dict
”的答案),如果您不想使用Counter
或defaultdict
,您也可以使用以下内容:
c = {}
s = 'AAABBBCAB'
psum = []
for char in s:
c[char] = c.get(char, 0) + 1
psum.append(c.copy())
尽管defaultdict
通常比dict.get(key, default)
更高效。
Counter
,一个简单的 defaultdict
就可以了 @hiro-protagonist,请查看我下面的答案! - Devesh Kumar Singhdefaultdict
比Counter
更“简单”?在哪方面更简单? - hiro protagonistdict
的子类;计数器的数据结构并不比dict
更复杂。我错了吗? - hiro protagonist您实际上甚至不需要一个计数器,只需要一个defaultdict就足够了!
from collections import defaultdict
c = defaultdict(int)
s = 'AAABBBCAB'
psum = []
#iterate through the character
for char in s:
#Update count for each character
c[char] +=1
#Add the updated dictionary to the output list
psum.append(dict(c))
print(psum)
[{'A': 1}, {'A': 2}, {'A': 3}, {'A': 3, 'B': 1},
{'A': 3, 'B': 2}, {'A': 3, 'B': 3},
{'A': 3, 'B': 3, 'C': 1}, {'A': 4, 'B': 3, 'C': 1},
{'A': 4, 'B': 4, 'C': 1}]
最简单的方法是使用collections模块中的Counter对象。
from collections import Counter
s = 'AAABBBCAB'
[ dict(Counter(s[:i]) for i in range(1,len(s))]
[{'A': 1}, {'A': 2}, {'A': 3}, {'A': 3, 'B': 1}, {'A': 3, 'B': 2},
{'A': 3, 'B': 3}, {'A': 3, 'B': 3, 'C': 1}, {'A': 4, 'B': 3, 'C': 1}]
Counter
是 dict
的一个子类,因此用普通的 dict
替换 Counter
的理由很少。 - chepner>>> from collections import Counter
>>> s = 'AAABBBCAB'
>>> c = Counter()
>>> [c := c + Counter(x) for x in s]
[Counter({'A': 1}), Counter({'A': 2}), Counter({'A': 3}), Counter({'A': 3, 'B': 1}), Counter({'A': 3, 'B': 2}), Counter({'A': 3, 'B': 3}), Counter({'A': 3, 'B': 3, 'C': 1}), Counter({'A': 4, 'B': 3, 'C': 1}), Counter({'A': 4, 'B': 4, 'C': 1})]