Когда я сталкиваюсь с этой проблемой, я думаю: «Я знаю, я буду использовать регулярные выражения».
Начните с составления списка всех паттернов, отсортированных по убыванию длины:
patterns = sorted(counts.keys(), key=len, reverse=True)
Теперь сделайте это в одном массивном регулярном выражении, которое является чередованием между каждым из шаблонов:
allPatterns = re.compile("|".join(patterns))
Теперь запустите этот шаблон над входной строкой и подсчитайте количество совпадений для каждого шаблона по ходу:
pos = 0
while (True):
match = allPatterns.search(inputString, pos)
if (match is None): break
pos = match.start() + 1
counts[match.group()] = counts[match.group()] + 1
В итоге вы получите количество каждой строки.
(Кроме того: я полагаю, что большинство хороших библиотек регулярных выражений скомпилируют большое чередование по фиксированным строкам, подобным этому, используя алгоритм Aho-Corasick, о котором упоминал e.dan. Использование библиотеки регулярных выражений, вероятно, самый простой способ применения этого алгоритма .)
С одной проблемой: где шаблон является префиксом другого шаблона (например, «первое» и «первое предложение»), только более длинный шаблон будет иметь счет против него. Это по замыслу: для этого и была сортировка по длине в начале.
Мы можем рассматривать это как этап постобработки; Пройдите подсчет, и всякий раз, когда один шаблон является префиксом другого, добавьте счет более длинного шаблона к более короткому шаблону. Будьте осторожны, чтобы не добавить дважды. Это просто сделано как вложенный цикл:
correctedCounts = {}
for donor in counts:
for recipient in counts:
if (donor.startswith(recipient)):
correctedCounts[recipient] = correctedCounts.get(recipient, 0) + counts[donor]
Этот словарь теперь содержит фактические значения.