Ваш текущий алгоритм работает примерно за O (| sent | * | фразу | * k), где k - это среднее количество слов в предложении. Ответ Патрика сводит это k к среднему количеству слов в фразе, которое в вашем случае должно быть меньше 10, так что это большое улучшение.
Улучшение худшего случая, вероятно, невозможно, но мы все же можем улучшить средний случай. Идея состоит в том, чтобы создать индекс со всеми словами, которые появляются в предложениях в качестве ключей, и список индексов предложений, которые имеют это слово в качестве значения.
После этого мы можем проверить заданную фразу, сколько предложений содержит каждое из ее слов, и просто выполнить итерацию по списку с меньшим количеством элементов. Например, если в вашей фразе есть слово, которого нет в предложении, мы избегаем полностью повторять предложения для этой фразы.
from collections import Counter
from collections import defaultdict
def containsQty(sentence, phrase):
qty = 100000
for word in phrase:
qty = min(qty, int(sentence[word] / phrase[word]))
if qty == 0:
break
return qty
sent = ["bob and alice like to text each other", "bob does not like to ski but does not like to fall", "alice likes to ski"]
ph = ["bob alice", "alice", "like"]
sent = [Counter(word.lower() for word in sentence.split()) for sentence in sent]
ph = [Counter(word.lower() for word in sentence.split()) for sentence in ph]
indexByWords = defaultdict(list)
for index, counter in enumerate(sent, start = 1):
for word in counter.keys():
indexByWords[word].append(index)
for i, phrase in enumerate(ph, start=1):
print("Phrase{}:".format(i))
best = None
minQty = len(sent) + 1
for word in phrase.keys():
if minQty > len(indexByWords[word]):
minQty = len(indexByWords[word])
best = indexByWords[word]
matched = False
for index in best:
qty = containsQty(sent[index - 1], phrase)
if qty > 0:
matched = True
print((str(index) + ' ') * qty)
if not matched:
print("NONE")