Эффективно найти, содержит ли строка группу символов (например, подстроку, но игнорируя порядок)?

Какой наиболее эффективный способ определить, существует ли группа символов, размещенная в строке, в строке в 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

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