Python — как найти все пересечения двух строк?
Как найти все пересечения (также называемые самыми длинными общими подстроками) двух строк и их положения в обеих строках?
Например, если S1=»never» и S2=»forever» , то полученное пересечение должно быть [«ever»] , а его позиции — [(1,3)] . Если S1=»address» и S2=»oddness» , то полученные пересечения — [«dd»,»ess»] , а их позиции — [(1,1),(4,4)] .
Кратчайшее решение без включения какой-либо библиотеки является предпочтительным. Но любое правильное решение также приветствуется.
6 ответов
Ну, вы говорите, что не можете включить ни одну библиотеку. Однако стандартный Python difflib содержит функцию, которая выполняет именно то, что вы ожидаете. Учитывая, что это вопрос интервью на Python, знакомство с difflib может оказаться тем, чего ожидал интервьюер.
In [31]: import difflib In [32]: difflib.SequenceMatcher(None, "never", "forever").get_matching_blocks() Out[32]: [Match(a=1, b=3, size=4), Match(a=5, b=7, size=0)] In [33]: difflib.SequenceMatcher(None, "address", "oddness").get_matching_blocks() Out[33]: [Match(a=1, b=1, size=2), Match(a=4, b=4, size=3), Match(a=7, b=7, size=0)]
Вы всегда можете игнорировать последний кортеж Match, так как он является фиктивным (согласно документации).
Я предполагаю, что вы хотите, чтобы подстроки совпадали, если они имеют одинаковую абсолютную позицию в своих соответствующих строках. Например, «abcd» и «bcde» не будут иметь совпадений, даже если оба содержат «bcd».
a = "address" b = "oddness" #matches[x] is True if a[x] == b[x] matches = map(lambda x: x[0] == x[1], zip(list(a), list(b))) positions = filter(lambda x: matches[x], range(len(a))) substrings = filter(lambda x: x.find("_") == -1 and x != "","".join(map(lambda x: ["_", a[x]][matches[x]], range(len(a)))).split("_"))
Если вам нужны только подстроки, вы можете поместить их в одну строку:
filter(lambda x: x.find("_") == -1 and x != "","".join(map(lambda x: ["_", a[x]][map(lambda x: x[0] == x[1], zip(list(a), list(b)))[x]], range(len(a)))).split("_"))
Модуль difflib может помочь вам — вот быстрый и грязный параллельный diff:
>>> import difflib >>> list(difflib.ndiff("never","forever")) ['- n', '+ f', '+ o', '+ r', ' e', ' v', ' e', ' r'] >>> diffs = list(difflib.ndiff("never","forever")) >>> for d in diffs: . print [d[0]]+d[1:] . n f o r e v e r
Это можно сделать в O (n + m), где n и m — длины входных строк.
function LCSubstr(S[1..m], T[1..n]) L := array(1..m, 1..n) z := 0 ret := <> for i := 1..m for j := 1..n if S[i] = T[j] if i = 1 or j = 1 L[i,j] := 1 else L[i,j] := L[i-1,j-1] + 1 if L[i,j] > z z := L[i,j] ret := <> if L[i,j] = z ret := ret ∪ return ret
def IntersectStrings( first, second): x = list(first) #print x y = list(second) lst1= [] lst2= [] for i in x: if i in y: lst1.append(i) lst2 = sorted(lst1) + [] # This above step is an optional if it is required to be sorted alphabetically use this or else remove it return ''.join(lst2) print IntersectStrings('hello','mello' )
import itertools def longest_common_substring(s1, s2): set1 = set(s1[begin:end] for (begin, end) in itertools.combinations(range(len(s1)+1), 2)) set2 = set(s2[begin:end] for (begin, end) in itertools.combinations(range(len(s2)+1), 2)) common = set1.intersection(set2) maximal = [com for com in common if sum((s.find(com) for s in common)) == -1 * (len(common)-1)] return [(s, s1.index(s), s2.index(s)) for s in maximal]
Проверка некоторых значений:
>>> longest_common_substring('address', 'oddness') [('dd', 1, 1), ('ess', 4, 4)] >>> longest_common_substring('never', 'forever') [('ever', 1, 3)] >>> longest_common_substring('call', 'wall') [('all', 1, 1)] >>> longest_common_substring('abcd1234', '1234abcd') [('abcd', 0, 4), ('1234', 4, 0)]
Python — как найти все пересечения двух строк?
Как найти все пересечения (также называемые самыми длинными обычными подстроками) двух строк и их позиции в обеих строках? Например, если S1=»never» и S2=»forever» , то приведенное пересечение должно быть [«ever»] , а его позиции [(1,3)] . Если S1=»address» и S2=»oddness» , то приведенные пересечения [«dd»,»ess»] , а их позиции [(1,1),(4,4)] . Предпочтительным является кратчайшее решение без включения какой-либо библиотеки. Но любое правильное решение также приветствуется.
Должны ли подстроки появляться в одной и той же позиции в обеих строках (как они делают в обоих ваших примерах)?
@aix: нет, они могут появляться в разных позициях, таких как «никогда» и «навсегда». Я изменил пример.
Что вы подразумеваете под «все решения», если для «call» и «wall» правильным решением является [«all»] а не [«a», «al», «all», «ll», «l», «l»] ? Вы имеете в виду все максимальные решения? Какое точное определение здесь?
@julkiewicz: 1: Да, максимальные решения. 2: не так много. Просто решение, если это возможно в Python.
@StevenRumbalski: это просто интересная проблема, я думаю, это очень весело — заниматься такими проблемами. Мне лично это очень нравится, и я уже решил это, но интересно, если кто-нибудь сможет найти альтернативное решение.
Головоломки Code Golf и программирование не по теме для этого сайта. Смотрите codegolf.stackexchange.com .
@StevenRumbalski: я обнаружил совершенно изумительный метод, но эта страница недостаточно велика, чтобы его вместить. Если серьезно, это очень долго и медленно.
6 ответов
Хорошо, вы говорите, что вы не можете включить какую-либо библиотеку. Тем не менее, стандарт Python difflib содержит функцию, которая делает именно то, что вы ожидаете. Учитывая, что это вопрос интервью на Python, знакомство с difflib может быть тем, что ожидал интервьюер.
In [31]: import difflib In [32]: difflib.SequenceMatcher(None, "never", "forever").get_matching_blocks() Out[32]: [Match(a=1, b=3, size=4), Match(a=5, b=7, size=0)] In [33]: difflib.SequenceMatcher(None, "address", "oddness").get_matching_blocks() Out[33]: [Match(a=1, b=1, size=2), Match(a=4, b=4, size=3), Match(a=7, b=7, size=0)]
Вы всегда можете игнорировать последний кортеж Match, поскольку он фиктивный (согласно документации).
Это можно сделать в O (n + m), где n и m — это длины входных строк.
function LCSubstr(S[1..m], T[1..n]) L := array(1..m, 1..n) z := 0 ret := <> for i := 1..m for j := 1..n if S[i] = T[j] if i = 1 or j = 1 L[i,j] := 1 else L[i,j] := L[i-1,j-1] + 1 if L[i,j] > z z := L[i,j] ret := <> if L[i,j] = z ret := ret ∪ return ret
Да, это была моя первая мысль, однако динамическое программирование обычно не является одной строкой.
Вот что я мог бы придумать:
import itertools def longest_common_substring(s1, s2): set1 = set(s1[begin:end] for (begin, end) in itertools.combinations(range(len(s1)+1), 2)) set2 = set(s2[begin:end] for (begin, end) in itertools.combinations(range(len(s2)+1), 2)) common = set1.intersection(set2) maximal = [com for com in common if sum((s.find(com) for s in common)) == -1 * (len(common)-1)] return [(s, s1.index(s), s2.index(s)) for s in maximal]
Проверка некоторых значений:
>>> longest_common_substring('address', 'oddness') [('dd', 1, 1), ('ess', 4, 4)] >>> longest_common_substring('never', 'forever') [('ever', 1, 3)] >>> longest_common_substring('call', 'wall') [('all', 1, 1)] >>> longest_common_substring('abcd1234', '1234abcd') [('abcd', 0, 4), ('1234', 4, 0)]
@agf вот что возвращается . или я что-то упустил? добавлен тест, который показывает, что он возвращает несколько значений
Обновлен мой ответ, чтобы включить индекс каждой подстроки в две строки. @psihodelia это то, что вы хотите?
Строки. Функции и методы строк
Итак, о работе со строками мы немного поговорили, теперь поговорим о функциях и методах строк.
Я постарался собрать здесь все строковые методы и функции, но если я что-то забыл — поправляйте.
Базовые операции
При вызове методов необходимо помнить, что строки в Python относятся к категории неизменяемых последовательностей, то есть все функции и методы могут лишь создавать новую строку.
: Поэтому все строковые методы возвращают новую строку, которую потом следует присвоить переменной.
Таблица «Функции и методы строк»
Функция или метод | Назначение |
---|---|
S = ‘str’; S = «str»; S = »’str»’; S = «»»str»»» | Литералы строк |
S = «s\np\ta\nbbb» | Экранированные последовательности |
S = r»C:\temp\new» | Неформатированные строки (подавляют экранирование) |
S = b»byte» | Строка байтов |
S1 + S2 | Конкатенация (сложение строк) |
S1 * 3 | Повторение строки |
S[i] | Обращение по индексу |
S[i:j:step] | Извлечение среза |
len(S) | Длина строки |
S.find(str, [start],[end]) | Поиск подстроки в строке. Возвращает номер первого вхождения или -1 |
S.rfind(str, [start],[end]) | Поиск подстроки в строке. Возвращает номер последнего вхождения или -1 |
S.index(str, [start],[end]) | Поиск подстроки в строке. Возвращает номер первого вхождения или вызывает ValueError |
S.rindex(str, [start],[end]) | Поиск подстроки в строке. Возвращает номер последнего вхождения или вызывает ValueError |
S.replace(шаблон, замена[, maxcount]) | Замена шаблона на замену. maxcount ограничивает количество замен |
S.split(символ) | Разбиение строки по разделителю |
S.isdigit() | Состоит ли строка из цифр |
S.isalpha() | Состоит ли строка из букв |
S.isalnum() | Состоит ли строка из цифр или букв |
S.islower() | Состоит ли строка из символов в нижнем регистре |
S.isupper() | Состоит ли строка из символов в верхнем регистре |
S.isspace() | Состоит ли строка из неотображаемых символов (пробел, символ перевода страницы (‘\f’), «новая строка» (‘\n’), «перевод каретки» (‘\r’), «горизонтальная табуляция» (‘\t’) и «вертикальная табуляция» (‘\v’)) |
S.istitle() | Начинаются ли слова в строке с заглавной буквы |
S.upper() | Преобразование строки к верхнему регистру |
S.lower() | Преобразование строки к нижнему регистру |
S.startswith(str) | Начинается ли строка S с шаблона str |
S.endswith(str) | Заканчивается ли строка S шаблоном str |
S.join(список) | Сборка строки из списка с разделителем S |
ord(символ) | Символ в его код ASCII |
chr(число) | Код ASCII в символ |
S.capitalize() | Переводит первый символ строки в верхний регистр, а все остальные в нижний |
S.center(width, [fill]) | Возвращает отцентрованную строку, по краям которой стоит символ fill (пробел по умолчанию) |
S.count(str, [start],[end]) | Возвращает количество непересекающихся вхождений подстроки в диапазоне [начало, конец] (0 и длина строки по умолчанию) |
S.expandtabs([tabsize]) | Возвращает копию строки, в которой все символы табуляции заменяются одним или несколькими пробелами, в зависимости от текущего столбца. Если TabSize не указан, размер табуляции полагается равным 8 пробелам |
S.lstrip([chars]) | Удаление пробельных символов в начале строки |
S.rstrip([chars]) | Удаление пробельных символов в конце строки |
S.strip([chars]) | Удаление пробельных символов в начале и в конце строки |
S.partition(шаблон) | Возвращает кортеж, содержащий часть перед первым шаблоном, сам шаблон, и часть после шаблона. Если шаблон не найден, возвращается кортеж, содержащий саму строку, а затем две пустых строки |
S.rpartition(sep) | Возвращает кортеж, содержащий часть перед последним шаблоном, сам шаблон, и часть после шаблона. Если шаблон не найден, возвращается кортеж, содержащий две пустых строки, а затем саму строку |
S.swapcase() | Переводит символы нижнего регистра в верхний, а верхнего – в нижний |
S.title() | Первую букву каждого слова переводит в верхний регистр, а все остальные в нижний |
S.zfill(width) | Делает длину строки не меньшей width, по необходимости заполняя первые символы нулями |
S.ljust(width, fillchar=» «) | Делает длину строки не меньшей width, по необходимости заполняя последние символы символом fillchar |
S.rjust(width, fillchar=» «) | Делает длину строки не меньшей width, по необходимости заполняя первые символы символом fillchar |
S.format(*args, **kwargs) | Форматирование строки |
Для вставки кода на Python в комментарий заключайте его в теги