Python Regex для соответствия ПЕРВОМ повторению цифры

Примеры:

  1. Для 0123123123 должна соответствовать 1, поскольку вторая 1 появляется перед повторением любой другой цифры.
  2. Для 01234554321 следует сопоставить 5, поскольку вторая 5 появляется перед повторением любой другой цифры.

Некоторые регулярные выражения, которые я пробовал:

  1. Нижеприведенное работает для 1-го, но не для 2-го примера. Вместо этого он соответствует 1, поскольку 1 — это первая цифра, которая появляется в строке, которая впоследствии повторяется.
import re
m = re.search(r"(\d).*?\1", string)
print(m.group(1))
  1. Нижеприведенное работает для второго, но не для первого примера. Вместо этого он соответствует 3 - в частности, 2-му и 3-му вхождению цифры. Я не знаю, почему он так себя ведет.
import re
m = re.search(r"(\d)(?!(\d).*?\2).*?\1", string)
print(m.group(1))

Может ли строка содержать что-либо кроме цифр?

SIGHUP 30.07.2024 10:27

Да, строка может содержать любой символ.

plsssineedheeeelp 30.07.2024 10:38

Есть ли причина, по которой регулярное выражение необходимо, когда гораздо более простой (и более эффективный) метод, предложенный @blhsing, явно превосходит?

SIGHUP 30.07.2024 10:44

Нет особой причины, по которой необходимо регулярное выражение. Когда я впервые столкнулся с проблемой, моей первой мыслью было, что будет быстрое и простое решение для регулярных выражений — я не ожидал, что оно окажется таким сложным.

plsssineedheeeelp 30.07.2024 10:59
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
13
5
791
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Ответ принят как подходящий

Одна идея: захватить конец строки и добавить его в отрицательный просмотр (здесь группа 2):

(\d)(?=.*?\1(.*))(?!.*?(\d).*?\3.+?\2$)

Таким образом, вы можете контролировать, где заканчивается подшаблон .*?(\d).*?\3 в отрицательном просмотре. Если .+?\2$ успешен, это означает, что есть еще одна цифра, которая повторяется перед цифрой в группе 1.

Я закрепил шаблон для демонстрации regex101 с помощью ^.*?, но вам не нужно делать это с помощью re.search метода.


Другой способ: переверните строку и найдите последнюю повторяющуюся цифру:

re.search(r'^.*(\d).*?\1', string[::-1]).group(1)

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

plsssineedheeeelp 30.07.2024 09:23

Отличная идея поработать с перевернутой струной! Думаю, не стоит лениться на втором .*?, так как первый уже «загоняет вас в угол».

DuesserBaest 31.07.2024 08:59

@DuesserBaest: точно, второй квантификатор не должен быть ленивым, поскольку он не должен быть жадным. Вы можете выбрать тот, который вам нужен.

Casimir et Hippolyte 31.07.2024 09:26

Regex может быть неподходящим инструментом для этой задачи. Хотя регулярное выражение в ответе @CasimiretHippolyte работает, довольно неэффективно сканировать всю остальную часть строки для каждого символа в строке до тех пор, пока не будет найден соответствующий символ, что обходится средней временной сложностью O(n ^ 2).

Более эффективный подход с линейной временной сложностью — использовать набор для отслеживания встретившихся символов и возвращать первый символ, уже добавленный в набор:

def first_repeating_digit(string):
    seen = set()
    for digit in filter(str.isdigit, string):
        if digit in seen:
            return digit
        seen.add(digit)
    raise ValueError('No repeating digit found.')

так что:

for s in '0123123123', '01234554321':
    print(s, first_repeating_digit(s))

выходы:

0123123123 1 
01234554321 5

Демо здесь

Бенчмарк-тест результат:

blhsing 0123123123 1.2911038296297193
blhsing 01234554321 1.3835312821902335
CasimiretHippolyte 0123123123 3.6279739402234554
CasimiretHippolyte 01234554321 4.1985282939858735

Если бы вы могли использовать модуль регулярных выражений PyPi , вы могли бы использовать утверждение просмотра назад с бесконечным квантором, а затем использовать re.search, чтобы получить первое совпадение.

(\d)(?<=\1\d+)
  • (\d) Захватите одну цифру в группе 1.
  • (?<= Позитивный взгляд назад: утверждать, что от текущей позиции влево
    • \1 Сопоставьте ту же цифру, что и в группе 1, используя обратную ссылку.
    • \d+ Сопоставьте 1 или более цифр
  • ) Закройте просмотр

См. демонстрацию регулярных выражений и демонстрацию Python.

Однострочное решение, подход аналогичный ответу @blhsing. Он получает первое вхождение с next(filter(... из x[1] (который является номером строки) в a[:x[0]], когда a — это строка, а x[0] — номер числа в строке, то есть a[x[0]] это в основном все числа, предшествующие x[1].

a = '01234554321'
print(next(filter(lambda x: x[1] in a[:x[0]], enumerate(a)))[1])

Другие вопросы по теме