Эффективно найти, содержит ли строка группу символов (например, подстроку, но игнорируя порядок)?
Какой наиболее эффективный способ определить, существует ли группа символов, размещенная в строке, в строке в Python?
Например, если у меня есть string="hello world"
и подстрока "roll"
, функция вернет true, потому что все 4 буквы в "roll"
существуют в "hello world"
.
Есть очевидная методология грубой силы, но мне было интересно, есть ли эффективный способ для Python для достижения этого.
EDIT: важно количество писем. Так, например, rollll
не входит в hello world
(только три l).
Ответы
Ответ 1
Вы можете использовать collections.Counter
:
from collections import Counter
substring_counts = Counter(substring)
text_counts = Counter(text)
if all(text_counts[letter] >= count for letter, count in substring_counts.items()):
# All the letters in `substring` are in `count`
Ответ 2
Для проверки "содержит" я обычно выбираю наборы:
set(string).issuperset(set(substring))
# or
set(string) >= set(substring)
Я не уверен в сложности здесь, но эта страница говорит, что установка и проверка надстроек - это O (n), так что это будет O (n + m), наравне с методом Даниэля Прайдена.
Как отмечено Kasramvd, вам не нужно создавать набор подстрок при использовании issuperset
:
set(string).issuperset(substring)
Использование >=
все еще нуждается в преобразовании.
Ответ 3
Создайте гистограмму символов в каждой строке, а затем вы можете проверить, происходит ли каждая буква в подстроке в большей строке. Runtime является линейным (O(n + m)
), а пространство пропорционально размеру алфавита.
Это форма подсчет сортировки.
Обратите внимание, что collections.Counter
- это структура данных гистограммы, поэтому алгоритм примерно одинаковый. Поскольку Counter
использует хеш-таблицу, она имеет пространственную сложность, пропорциональную количеству фактически встречающихся элементов (букв), но с более высокими постоянными факторами, чем подход с голубями, поэтому Counter
немного менее эффективен, но вряд ли будет заметно.
Ответ 4
Используйте концепцию Hashing
:
В python Hashing
реализуется с помощью dict()
hashmap = dict()
string = "hello world"
substring = "roll"
for char in string:
if char in hashmap:
hashmap[char] += 1
else:
hashmap[char] = 1
flag = 0
for char in substring:
if char in hashmap and hashmap[char] >= 1:
hashmap[char] -= 1
else:
flag = 1
break
if flag == 1:
print False
else:
print True
Для символов в строке мы создаем хэш-карту, в которой хранится запись различных доступных символов и их соответствующих счетчиков.
Затем мы перебираем подстроку и выясняем, доступны ли все символы или нет. Если доступно, мы уменьшаем счетчик этого символа в hashmap и продвигаемся вперед. ЕСЛИ нет, то просто break
выйдите и распечатайте False
..... Так просто
Надеюсь, это поможет!!!
Ответ 5
Если вы хотите повысить эффективность, вы можете построить Counter
, который подсчитывает все буквы в string
и уменьшает эти подсчеты для каждой буквы в substring
. Если какое-либо количество меньше 0, для substring
не хватает экземпляров символа в string
. Это алгоритм O (string
+ substring
).
from collections import Counter
def unordered_substring(string, substring):
string_counter = Counter(string)
for char in substring:
string_counter[char] -= 1
if string_counter[char] < 0:
return False
return True
В случае, когда string
длинный и substring
часто будет найден, вы можете отменить метод для подсчета букв в substring
, а затем перебрать string
, сломав, когда все символы будут найдены.
def unordered_substring_long(string, substring):
substring_counter = Counter(substring)
total_count = sum(substring_counter.values())
for char in string:
if substring_counter[char] > 0:
substring_counter[char] -= 1
total_count -= 1
if total_count == 0:
return True
return False
Ответ 6
Вы можете использовать что-то вроде этого, которое True
тогда и только тогда, когда каждый символ в sub
существует в s
, как указано OP:
all(sub.count(i) <= s.count(i) for i in set(sub))
it True
, если для каждого отдельного символа в sub
по крайней мере столько из них в s
, как в sub
.
Ответ 7
Я думаю, что лучше перебирать строку sub, а не сама строка. И, как более пифонический подход, вы можете использовать выражение-генератор в sum
, суммируя количество прописных букв в основной строке. Затем сравните его с длиной подстроки:
sum(i in s for i in sub) == len(sub)
Как вы можете видеть это намного быстрее, чем подход counter
и set
, когда ваша строка становится больше:
In [45]: s = "hello world"
In [46]: sub = "roll"
In [47]: s *= 1000
In [48]: %timeit set(s).issuperset(sub)
10000 loops, best of 3: 136 µs per loop
In [53]: %timeit substring_counts = Counter(sub); text_counts = Counter(s); all(text_counts[letter] >= count for letter, count in substring_counts.items())
100 loops, best of 3: 2.21 ms per loop
In [49]: %timeit sum(i in s for i in sub) == len(sub)
1000000 loops, best of 3: 739 ns per loop
Ответ 8
Первое, что мне кажется, это то, что вы можете легко взять индивидуальный счетчик исходной строки, а затем взять количество символов каждой подстроки. Затем перекрестите с количеством символов исходной строки, чтобы увидеть, присутствует ли количество символов каждого типа, необходимое для создания вашей подстроки. Это очень простой способ найти, является ли данная строка анаграммой другой строки. То же самое можно применить к вашему сценарию.