Можно ли решить LeetCode 1653 с помощью рекурсии?

Я пытаюсь решить проблему LeetCode 1653. Минимальное количество удалений для балансировки строк:

Вам дана строка s, состоящая только из символов 'a' и 'b'​​​​.

Вы можете удалить любое количество символов в s, чтобы сделать s сбалансированным. s сбалансирован, если не существует пары индексов (i,j) такой, что i < j и s[i] = 'b' и s[j]= 'a'.

Верните минимальное количество удалений, необходимое для балансировки s.

Ограничения:

  • 1 <= s.length <= 105
  • s[i] — это 'a' или 'b'​​.

Пример 1:

Ввод: s = "aababbab"
Вывод: 2
Пояснение: Вы можете:
Удалите символы в позициях 2 и 6 с нулевым индексом («aababbab» -> «aaabbb»), или
Удалите символы в позициях 3 и 6 с нулевым индексом («aababbab» -> «aabbbb»)

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

Я изначально сделал так:

class Solution:
    def minimumDeletions(self, s: str) -> int:

        @lru_cache(None)
        def dfs(index, last_char):
            if index == len(s):
                return 0

            if s[index] >= last_char:
                keep = dfs(index + 1, s[index])
                delete = 1 + dfs(index + 1, last_char)
                return min(keep, delete)
            else:
                return 1 + dfs(index + 1, last_char)
        return dfs(0, 'a')

Но это не было сокращением путей, которые уже превышают ранее найденный минимум. Достаточно справедливо, поэтому я попробовал следующее:

class Solution:
    def minimumDeletions(self, s: str) -> int:
        self.min_deletions = float('inf') 
        memo = {}

        def dfs(index, last_char, current_deletions):
            if current_deletions >= self.min_deletions:
                return float('inf')
            
            if index == len(s):
                self.min_deletions = min(self.min_deletions, current_deletions)
                return 0

            if (index, last_char) in memo:
                return memo[(index, last_char)]

            if s[index] >= last_char:
                keep = dfs(index + 1, s[index], current_deletions)
                delete = 1 + dfs(index + 1, last_char, current_deletions + 1)
                result = min(keep, delete)
            else:
                result = 1 + dfs(index + 1, last_char, current_deletions + 1)

            memo[(index, last_char)] = result
            return result

        return dfs(0, 'a', 0)

Кажется, он проходит тестовые случаи, когда я пытаюсь запустить его на 300 мс, но когда я пытаюсь отправить решение, я получаю ошибку превышения лимита памяти. Как это можно решить с помощью рекурсии в пределах срока?

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

Woodford 31.07.2024 19:16

Я думаю, что вы можете преобразовать memo из набора в np.array of len [2,len(s)], а затем сопоставить 'a' с нулем и 'b' с 1. так что (a,13) будет memo[0, 13]. Это должно немного помочь с уменьшением памяти.

Johnny Cheesecutter 31.07.2024 19:28

Также я бы предложил идти с правой стороны массива, а не с левой. Если вы пойдете с правой стороны, вам нужно будет сохранить только memo['a'] и memo['b'], всего четыре значения (лучший результат и количество удалений).

Johnny Cheesecutter 31.07.2024 19:32

@JohnnyCheesecutter Спасибо, ваше предложение сработало, я попробовал заменить dict массивом numpy, и решение было принято <3

Vanillaice 31.07.2024 21:04

@Vanillaice, пожалуйста, не размещайте ответ на свой вопрос в исходном сообщении. Вместо этого опубликуйте свой ответ в разделе ответов. В Stack Overflow все работает именно так: вопрос и ответ размещаются отдельно в своих разделах.

trincot 31.07.2024 21:05
Почему в 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 может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
5
56
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Именно объем памяти, необходимый для вашей структуры данных memo, является основным вкладом в получаемую вами ошибку.

Вы можете избежать ввода кортежа и заранее выделить memo в виде двух списков, например:

        memo = {
            "a": [None] * len(s),
            "b": [None] * len(s)
        }

... и адаптируйте свой код в соответствии с этой структурой. Так:

            if memo[last_char][index] is not None:
                return memo[last_char][index]
            # ...
            # ...
            return memo[last_char][index]

Тогда он пройдет испытания.

Не связано с потреблением памяти, но вы рассматриваете слишком много возможностей. В случае s[index] == last_char нет необходимости проверять два варианта (удалить или не удалять), так как можно не удалять. Нет необходимости рассматривать случай удаления. Итак, вы можете сделать:

            if s[index] > last_char:  # Altered condition
                keep = dfs(index + 1, s[index], current_deletions)
                delete = 1 + dfs(index + 1, last_char, current_deletions + 1)
                result = min(keep, delete)
            elif s[index] == last_char:  # No need to delete
                result = dfs(index + 1, last_char, current_deletions)
            else:
                result = 1 + dfs(index + 1, last_char, current_deletions + 1)

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