Как эффективно вычислить префиксную сумму частот символов в строке?

Скажем, у меня есть строка

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}]

Наконец, вам нужен один диктофон или вам нужен список диктовок для каждого символа во время чтения?

Vanjith 29.04.2019 15:22

@Vanjith Мне нужен счетчик частот символов.

planetp 29.04.2019 15:43
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
23
2
2 105
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

это вариант:

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в ответе Евгения Ярмаша; я просто избегаю 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 Singh 29.04.2019 15:40

почему вы говорите, что defaultdict «проще», чем Counter? в чем проще?

hiro protagonist 29.04.2019 15:41

Я не сказал проще, я сказал defaultdict — это более простая структура данных для представления словаря, чем Counter, имеет ли это смысл?

Devesh Kumar Singh 29.04.2019 15:45

@DeveshKumarSingh они оба являются подклассами dict; структура данных счетчика не сложнее, чем у dict. или что я упускаю?

hiro protagonist 29.04.2019 15:52

@DeveshKumarSingh, эти соображения неуместны. Я указал разницу во времени, но ОП должен принять собственное решение.

RomanPerekhrest 29.04.2019 17:03

с небольшим риском неправильного использования вы можете сделать это генератором и выдавать исходный счетчик на каждом этапе, отправляя, возможно, дорогую копию dict потребителю, которому может не понадобиться

WorldSEnder 29.04.2019 21:18

@WorldSEnder уверен, что это возможно. я создал список, поскольку вопрос предполагал, что это будет желаемый результат...

hiro protagonist 29.04.2019 21:38

@DeveshKumarSingh: Ваш ответ пришел позже, чем этот, это точно такая же структура, но немного другого типа, она имеет ту же сложность, но с более подробным выводом. Вы не должны рекламировать это здесь.

Eric Duminil 29.04.2019 22:11

О каком ответе вы говорите Эрику, я не вижу ни одного от вас в треде здесь! Кроме того, мой комментарий уже был отмечен как неуместное соображение!

Devesh Kumar Singh 30.04.2019 02:13

@DeveshKumarSingh: я не написал никакого ответа, потому что Хиро и Юджин достаточно хороши. Если вы согласны с тем, что ваш комментарий был неуместен, вы всегда можете удалить его.

Eric Duminil 30.04.2019 08:53

Использование dict было бы более эффективным подходом, чем использование defaultdict или Counter, поскольку преобразование в конце (в dict) требует больших усилий.

NemPlayer 23.08.2019 19:35

Проще всего было бы использовать объект 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, поэтому нет особых причин заменять Counter простым dict.

chepner 29.04.2019 15:21

Я согласен, но это больше соответствует тому, что пользователь указал в качестве вывода. Я бы оставил объекты Counter у себя, так как у них есть полезные функции в дополнение к тому, что они являются диктовками.

Christian Sloper 29.04.2019 15:22

Это элегантный 1-лайнер, поэтому +1, но он квадратичный, а не линейный. Подозреваю, что аналогичное решение от hiro protagonist более эффективно.

John Coleman 29.04.2019 15:22
Ответ принят как подходящий

Вы можете сделать это в одной строке, используя itertools.accumulate и collections.Counter:

from collections import Counter
from itertools import accumulate

s = 'AAABBBCAB'
psum = list(accumulate(map(Counter, s)))

Это дает вам список Counter объектов. Теперь, чтобы получить частоты для любой подстроки s за время O(1), вы можете просто вычесть счетчики, например:

>>> psum[6] - psum[1]  # get frequencies for s[2:7]
Counter({'B': 3, 'A': 1, 'C': 1})

На самом деле вам даже не нужен счетчик для этого, достаточно будет 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}]

В Python 3.8 вы можете использовать понимание списка с выражение присваивания (он же «оператор моржа»):

>>> 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})]

Другие вопросы по теме