Как я могу использовать Regex для поиска строки символов в алфавитном порядке с помощью Python?

Итак, у меня есть задача, над которой я работаю - найти самую длинную строку алфавитных символов в строке. Например, "abcghiijkyxz" должен привести к "ghiijk" (Да, я удваивается).

Я делал совсем немного с циклами для решения проблемы - итерацией по всей строке, затем для каждого символа, начиная второй цикл с использованием lower и ord. Никакой помощи не требуется писать этот цикл.

Однако мне было предложено, чтобы Regex был бы хорош для такого рода вещей. Мое регулярное выражение слабое (я знаю, как схватить статический набор, мои знания о переходе вперед расширяются, зная, что они существуют). Как мне написать регулярное выражение, чтобы смотреть вперед и проверять будущие символы, чтобы быть следующим в алфавитном порядке? Или предложение использовать Regex непрактично для этого типа вещей?

Изменить: общее мнение похоже, что Regex действительно ужасно для такого типа вещей.

Ответ 1

Чтобы продемонстрировать, почему регулярное выражение не практично для такого рода вещей, вот регулярное выражение, которое будет соответствовать ghiijk в вашем примере abcghiijkyxz. Обратите внимание, что он также будет соответствовать abc, y, x, z, так как они должны технически считаться для самой длинной строки алфавитных символов по порядку. К сожалению, вы не можете определить, какой из них длиннее с регулярным выражением, но это дает вам все возможности. Обратите внимание, что это регулярное выражение работает для PCRE и не будет работать с модулем python re! Также обратите внимание, что в настоящее время библиотека python regex не поддерживает (*ACCEPT).

См. здесь выражение regex

((?:a+(?(?!b)(*ACCEPT))|b+(?(?!c)(*ACCEPT))|c+(?(?!d)(*ACCEPT))|d+(?(?!e)(*ACCEPT))|e+(?(?!f)(*ACCEPT))|f+(?(?!g)(*ACCEPT))|g+(?(?!h)(*ACCEPT))|h+(?(?!i)(*ACCEPT))|i+(?(?!j)(*ACCEPT))|j+(?(?!k)(*ACCEPT))|k+(?(?!l)(*ACCEPT))|l+(?(?!m)(*ACCEPT))|m+(?(?!n)(*ACCEPT))|n+(?(?!o)(*ACCEPT))|o+(?(?!p)(*ACCEPT))|p+(?(?!q)(*ACCEPT))|q+(?(?!r)(*ACCEPT))|r+(?(?!s)(*ACCEPT))|s+(?(?!t)(*ACCEPT))|t+(?(?!u)(*ACCEPT))|u+(?(?!v)(*ACCEPT))|v+(?(?!w)(*ACCEPT))|w+(?(?!x)(*ACCEPT))|x+(?(?!y)(*ACCEPT))|y+(?(?!z)(*ACCEPT))|z+(?(?!$)(*ACCEPT)))+)

Результаты в:

abc
ghiijk
y
x
z

Объяснение одной опции, т.е. a+(?(?!b)(*ACCEPT)):

  • a+ Соответствует a (буквально) один или несколько раз. Это вызывает экземпляры, в которых несколько одинаковых символов находятся в последовательности, например aa.
  • (?(?!b)(*ACCEPT)) Если предложение оценивает условие.
    • (?!b) Условие для предложения if. Отрицательный прогноз, обеспечивающий следующее: не b. Это связано с тем, что если это не b, мы хотим, чтобы следующий управляющий глагол вступил в силу.
    • (*ACCEPT) Если условие (выше) выполнено, мы принимаем текущее решение. Этот контрольный глагол заставляет регулярное выражение успешно завершаться, пропуская остальную часть шаблона. Поскольку этот токен находится внутри группы захвата, только эта группа захвата успешно завершена в этом конкретном месте, в то время как родительский шаблон продолжает выполняться.

Итак, что произойдет, если условие не выполнено? Ну, это означает, что (?!b) оценивается как false. Это означает, что на самом деле следующий символ b, и поэтому мы разрешаем продолжить сопоставление (скорее, захват в этом случае). Обратите внимание, что весь шаблон завернут в (?:)+, что позволяет нам сопоставлять последовательные параметры до тех пор, пока не будет выполнен контрольный глагол (*ACCEPT) или конец строки.

Единственное исключение для всего этого регулярного выражения - это выражение z. Поскольку это последний символ английского алфавита (который, как я полагаю, является целью этого вопроса), нам все равно, что происходит после этого, поэтому мы можем просто положить z+(?(?!$)(*ACCEPT)), который не обеспечит ничего после z. Если вы вместо этого хотите сопоставить za (круговое алфавитное соответствие порядка - idk, если это правильная терминология, но это звучит правильно для меня), вы можете использовать z+(?(?!a)(*ACCEPT)))+, как показано .

Ответ 2

Как уже упоминалось, regex не лучший инструмент для этого. Поскольку вас интересует непрерывная последовательность, вы можете сделать это с помощью одного цикла:

def LNDS(s):
    start = 0
    cur_len = 1
    max_len = 1
    for i in range(1,len(s)):
        if ord(s[i]) in (ord(s[i-1]), ord(s[i-1])+1):
            cur_len += 1
        else:
            if cur_len > max_len:
                max_len = cur_len
                start = i - cur_len
            cur_len = 1
    if cur_len > max_len:
        max_len = cur_len
        start = len(s) - cur_len
    return s[start:start+max_len]

>>> LNDS('abcghiijkyxz')
'ghiijk'

Мы сохраняем текущее количество всех неубывающих символов, которые мы видели, и когда закончившаяся неубывающая последовательность заканчивается, мы сравниваем ее с самой длинной неубывающей последовательностью, которую мы видели ранее, обновляя нашу "лучшую видимую до сих пор", если он длиннее.

Ответ 3

Regex: char+ Значение a+b+c+...

Подробнее:

  • + Совпадение между одним и неограниченным временем

Код Python:

import re

def LNDS(text):
    array = []

    for y in range(97, 122):  # a - z
        st = r"%s+" % chr(y)
        for x in range(y+1, 123):  # b - z
            st += r"%s+" % chr(x)
            match = re.findall(st, text)

            if match:
                array.append(max(match, key=len))
            else:
                break

        if array:
            array = [max(array, key=len)]

    return array

Выход

print(LNDS('abababababab abc')) >>> ['abc']
print(LNDS('abcghiijkyxz')) >>> ['ghiijk']

Для строки abcghiijkyxz шаблон регулярного выражения:

a+b+                    i+j+k+l+
a+b+c+                  j+k+
a+b+c+d+                j+k+l+
b+c+                    k+l+
b+c+d+                  l+m+
c+d+                    m+n+
d+e+                    n+o+
e+f+                    o+p+
f+g+                    p+q+
g+h+                    q+r+
g+h+i+                  r+s+
g+h+i+j+                s+t+
g+h+i+j+k+              t+u+
g+h+i+j+k+l+            u+v+
h+i+                    v+w+
h+i+j+                  w+x+
h+i+j+k+                x+y+
h+i+j+k+l+              y+z+
i+j+
i+j+k+

Демо-версия кода

Ответ 4

Сгенерируйте все подстроки регулярных выражений, такие как ^ a + b + c + $(от самого длинного до кратчайшего). Затем сопоставьте каждое из этих регулярных выражений со всеми подстроками (от самого длинного до кратчайшего) от "abcghiijkyxz" и остановитесь в первом матче.

def all_substrings(s):
    n = len(s)
    for i in xrange(n, 0, -1):
        for j in xrange(n - i + 1):
            yield s[j:j + i]

def longest_alphabetical_substring(s):
    for t in all_substrings("abcdefghijklmnopqrstuvwxyz"):
        r = re.compile("^" + "".join(map(lambda x: x + "+", t)) + "$")
        for u in all_substrings(s):
            if r.match(u):
                return u

print longest_alphabetical_substring("abcghiijkyxz")

Это печатает "ghiijk".

Ответ 5

Чтобы "решить" проблему, вы можете использовать

string = 'abcxyzghiijkl'

def sort_longest(string):
    stack = []; result = [];

    for idx, char in enumerate(string):
        c = ord(char)
        if idx == 0:
            # initialize our stack
            stack.append((char, c))
        elif idx == len(string) - 1:
            result.append(stack)
        elif c == stack[-1][1] or c == stack[-1][1] + 1:
            # compare it to the item before (a tuple)
            stack.append((char, c))
        else:
            # append the stack to the overall result
            # and reinitialize the stack
            result.append(stack)
            stack = []
            stack.append((char, c))

    return ["".join(item[0]
        for item in sublst) 
        for sublst in sorted(result, key=len, reverse=True)]

print(sort_longest(string))

Что дает

['ghiijk', 'abc', 'xyz']

в этом примере.


Идея состоит в том, чтобы перебирать строку и отслеживать переменную stack, которая заполняется вашими требованиями с помощью ord().