Я пытаюсь решить проблему LeetCode 1653. Минимальное количество удалений для балансировки строк:
Вам дана строка
s, состоящая только из символов'a'и'b'.Вы можете удалить любое количество символов в
s, чтобы сделатьsсбалансированным.sсбалансирован, если не существует пары индексов(i,j)такой, чтоi < jиs[i] = 'b'иs[j]= 'a'.Верните минимальное количество удалений, необходимое для балансировки
s.Ограничения:
1 <= s.length <= 105s[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 мс, но когда я пытаюсь отправить решение, я получаю ошибку превышения лимита памяти. Как это можно решить с помощью рекурсии в пределах срока?
Я думаю, что вы можете преобразовать memo из набора в np.array of len [2,len(s)], а затем сопоставить 'a' с нулем и 'b' с 1. так что (a,13) будет memo[0, 13]. Это должно немного помочь с уменьшением памяти.
Также я бы предложил идти с правой стороны массива, а не с левой. Если вы пойдете с правой стороны, вам нужно будет сохранить только memo['a'] и memo['b'], всего четыре значения (лучший результат и количество удалений).
@JohnnyCheesecutter Спасибо, ваше предложение сработало, я попробовал заменить dict массивом numpy, и решение было принято <3
@Vanillaice, пожалуйста, не размещайте ответ на свой вопрос в исходном сообщении. Вместо этого опубликуйте свой ответ в разделе ответов. В Stack Overflow все работает именно так: вопрос и ответ размещаются отдельно в своих разделах.






Именно объем памяти, необходимый для вашей структуры данных 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)
DP может быть итеративным или рекурсивным, поэтому не исключайте стратегии DP только потому, что вы выбрали рекурсивный подход. Подсказка: обратите внимание, что ключевым элементом DP является мемоизация.