Задача с собеседования Яндекс
Задача
Дана последовательность a и последовательность a'. Вам требуется найти такие l, r, что отсортировав подмассив a[l:r+1], можно получить массив a'. Если ответов несколько, выберите отрезок наибольшей длины. По условию такая операция всегда существует.
Чат алгоритмистов
Решение
Найдем первое отличающееся вхождение, позицию i: a[i]!= a'[i](то есть самое левое) и найдем последнее (самое правое). Далее расширим эти границы: до тех пор, пока левее от левой границы в приведенном массиве a стоит меньше или равный a'[l-1]<= a'[l], будем двигать его влево. Аналогично и с правым указателем, расширим его вправо.
Код
l,r=-1,-1
for i in range(n):
if a[i]!=aa[i]:
r=i
if l==-1:
l=i
while l>0 and aa[l-1]<=aa[l]:
l-=1
while r<n-1 and aa[r+1]>=aa[r]:
r+=1
print(l+1,r+1)
@algoses
Задача
Дана последовательность a и последовательность a'. Вам требуется найти такие l, r, что отсортировав подмассив a[l:r+1], можно получить массив a'. Если ответов несколько, выберите отрезок наибольшей длины. По условию такая операция всегда существует.
Чат алгоритмистов
Решение
Код
for i in range(n):
if a[i]!=aa[i]:
r=i
if l==-1:
l=i
while l>0 and aa[l-1]<=aa[l]:
l-=1
while r<n-1 and aa[r+1]>=aa[r]:
r+=1
print(l+1,r+1)
@algoses
❤14🔥1👏1
Задача с собеса в Авито
Условие задачи
Дано бинарное поисковое дерево (BST) и число k. Нужно вернуть значение k-го по величине элемента в этом дереве (нумерация с 1).
для любой вершины: все значения в левом поддереве меньше её значения; все значения в правом поддереве больше её значения.
Решение
Очень важное свойство BST: если обойти дерево в порядке
лево -> корень -> право (симметричный / inorder обход),
то значения вершин будут посещаться в отсортированном по возрастанию порядке.
Значит, k-й посещённый элемент при таком обходе — это и есть k-й по величине.
Делаем итеративный inorder-обход через стек:
1. Идём по указателю node максимально влево, по пути кладём все вершины в стек.
2. Когда упёрлись в None, достаём вершину из стека — это следующий по возрастанию элемент.
3. Уменьшаем k. Если k стало 0 — возвращаем значение этой вершины.
4. Переходим в правого ребёнка и повторяем.
Так мы не храним все элементы, а только путь в стеке.
Сложность:
по времени: O(h + k), где h — высота дерева (в худшем O(n), для сбалансированного O(log n)), по памяти: O(h) на стек
Код:
class Solution:
def kthSmallest(self, root: Optional['TreeNode'], k: int) -> int:
stack = []
node = root
while stack or node:
while node:
stack.append(node)
node = node.left
node = stack.pop()
k -= 1
if k == 0:
return node.val
node = node.right
@algoses
Условие задачи
Дано бинарное поисковое дерево (BST) и число k. Нужно вернуть значение k-го по величине элемента в этом дереве (нумерация с 1).
для любой вершины: все значения в левом поддереве меньше её значения; все значения в правом поддереве больше её значения.
Решение
лево -> корень -> право (симметричный / inorder обход),
то значения вершин будут посещаться в отсортированном по возрастанию порядке.
Значит, k-й посещённый элемент при таком обходе — это и есть k-й по величине.
Делаем итеративный inorder-обход через стек:
1. Идём по указателю node максимально влево, по пути кладём все вершины в стек.
2. Когда упёрлись в None, достаём вершину из стека — это следующий по возрастанию элемент.
3. Уменьшаем k. Если k стало 0 — возвращаем значение этой вершины.
4. Переходим в правого ребёнка и повторяем.
Так мы не храним все элементы, а только путь в стеке.
Сложность:
Код:
class Solution:
def kthSmallest(self, root: Optional['TreeNode'], k: int) -> int:
stack = []
node = root
while stack or node:
while node:
stack.append(node)
node = node.left
node = stack.pop()
k -= 1
if k == 0:
return node.val
node = node.right
@algoses
🔥11❤3👏1😁1🗿1
Forwarded from Поступашки - ШАД, Стажировки и Магистратура
Поступашки продолжают набор на новую линейку карьерных курсов 🎉
Мечтаешь стать крутым специалистом и с легкость тащить собесы, но не хватает фундамента? Хочешь овладеть знаниями и навыками для работы в крупной компании как Яндекс, Тинькофф или ВК? Узнал себя? Тогда записывайся у администратора на любой из курсов (если андроид - смотрим через яндекс браузер):
➡️ SQL и базы данных
➡️ анализ временных рядов
➡️ обучение с подкреплением
Все курсы стартуют 29.11. Курсы заточены на практику, вся теория будет разобрана на конкретных задачах и кейсах, с которыми сталкиваются на работе и на собесах. Ничего нудного и скучного! Изучаем только то, что тебе реально понадобится и залетаем на первую работу! Хочешь подробностей? На нашем сайте можно найти программу и отзывы на каждый курс.
Помимо этого на курсах тебя ждут:
- пет проекты и мини проекты, которые пойдут в портфолио;
- разбор реальных тестовых заданий бигтехов;
- разбор актуального контеста на стажировку в Яндекс и Тинькофф;
- банк реальных технических вопрос с собесов;
- разбор всех задач с алгособесов Яндекса!
А после прохождения курса тебя ждет пробный собес с подробной консультацией и сопровождением, рефералкой в Яндекс или в другие топовые компании!
📊 Цена 10'000р 6'990р! Хочешь купить несколько курсов сразу? Дадим хорошую скидку!
Для вопросов и покупок пишем администратору и не тянем с этим: на каждом курсе количество мест ограничено
Мечтаешь стать крутым специалистом и с легкость тащить собесы, но не хватает фундамента? Хочешь овладеть знаниями и навыками для работы в крупной компании как Яндекс, Тинькофф или ВК? Узнал себя? Тогда записывайся у администратора на любой из курсов (если андроид - смотрим через яндекс браузер):
Все курсы стартуют 29.11. Курсы заточены на практику, вся теория будет разобрана на конкретных задачах и кейсах, с которыми сталкиваются на работе и на собесах. Ничего нудного и скучного! Изучаем только то, что тебе реально понадобится и залетаем на первую работу! Хочешь подробностей? На нашем сайте можно найти программу и отзывы на каждый курс.
Помимо этого на курсах тебя ждут:
- пет проекты и мини проекты, которые пойдут в портфолио;
- разбор реальных тестовых заданий бигтехов;
- разбор актуального контеста на стажировку в Яндекс и Тинькофф;
- банк реальных технических вопрос с собесов;
- разбор всех задач с алгособесов Яндекса!
А после прохождения курса тебя ждет пробный собес с подробной консультацией и сопровождением, рефералкой в Яндекс или в другие топовые компании!
Для вопросов и покупок пишем администратору и не тянем с этим: на каждом курсе количество мест ограничено
Please open Telegram to view this post
VIEW IN TELEGRAM
😁4❤1
Разбор на стажировку Яндекса
Финальные 6 часов скидок на наши курсы подходят к концу. В честь этого выкладываем разбор актуального контеста в Яндекс:
— Бэкенд
— Аналитика
— ML & DS
Обязательно пересылайте такую годноту своим друзьям и одногруппникам в чаты. Больше подписчиков — больше разборов.
@postypashki_old
Финальные 6 часов скидок на наши курсы подходят к концу. В честь этого выкладываем разбор актуального контеста в Яндекс:
— Бэкенд
— Аналитика
— ML & DS
Обязательно пересылайте такую годноту своим друзьям и одногруппникам в чаты. Больше подписчиков — больше разборов.
@postypashki_old
😁3❤2🔥1🙉1
Задача с собеседования в Яндекс
Дан отсортированный в порядке возрастания массив nums, состоящий из уникальных целых чисел.
Известно, что диапазон [a, b] представляет собой множество всех целых чисел от a до b включительно.
Вам необходимо вернуть наименьший отсортированный список диапазонов, в котором каждый элемент массива находится в одном из диапазонов. И нет такого числа, которое есть в диапазоне, но отсутствует в изначальном массиве nums.
Пример:
Input: nums = [0,2,3,4,6,8,9]
Output: ["0","2->4","6","8->9"]
Чат алгоритмистов
Решение
Проходим внешним циклом по всем элементам массива.
start - начало текущего диапазона. С помощью внутреннего цикла ищем окончание диапазона (проверяем наличие следующего эл-та и то, что числа идут подряд, если оба условия верны: обновляем i).
Если начало диапазона не равняется текущему значению nums[i]: выводим (строковый) диапазон, состоящий из более чем одного числа. Иначе: диапазон состоит из одного числа. Переходим к следующему эл-ту (за пределами выведенного диапазона).
Сложность
O(n) - по времени
O(n) - по памяти
Код
def summaryRanges(self, nums: List[int]) -> List[str]:
result = []
i = 0
while i < len(nums):
start = nums[i]
while i < len(nums) - 1 and nums[i] + 1 == nums[i + 1]:
i+= 1
if start != nums[i]:
result.append(str(start) + "->" + str(nums[i]))
else:
result.append(str(nums[i]))
i += 1
return result
@algoses
Дан отсортированный в порядке возрастания массив nums, состоящий из уникальных целых чисел.
Известно, что диапазон [a, b] представляет собой множество всех целых чисел от a до b включительно.
Вам необходимо вернуть наименьший отсортированный список диапазонов, в котором каждый элемент массива находится в одном из диапазонов. И нет такого числа, которое есть в диапазоне, но отсутствует в изначальном массиве nums.
Пример:
Input: nums = [0,2,3,4,6,8,9]
Output: ["0","2->4","6","8->9"]
Чат алгоритмистов
Решение
start - начало текущего диапазона. С помощью внутреннего цикла ищем окончание диапазона (проверяем наличие следующего эл-та и то, что числа идут подряд, если оба условия верны: обновляем i).
Если начало диапазона не равняется текущему значению nums[i]: выводим (строковый) диапазон, состоящий из более чем одного числа. Иначе: диапазон состоит из одного числа. Переходим к следующему эл-ту (за пределами выведенного диапазона).
Сложность
O(n) - по времени
O(n) - по памяти
Код
result = []
i = 0
while i < len(nums):
start = nums[i]
while i < len(nums) - 1 and nums[i] + 1 == nums[i + 1]:
i+= 1
if start != nums[i]:
result.append(str(start) + "->" + str(nums[i]))
else:
result.append(str(nums[i]))
i += 1
return result
@algoses
🔥5🥱5❤2👏1
Состоялся первый студенческий хакатон для мессенджера МАХ — участники привезли на очный финал в Москву больше 50 сервисов, и жюри выбрало девять лучших. В треке «Цифровизация» выиграла команда SUPERVIBECODING с сервисом «Цифровой кампус», в социальном треке — Smolensk Dynamics, разработавшая бота для анализа состава продуктов питания, а в «Эффективности» сильнейшими стали ребята из Explain с AI-календарём.
Хакатон получился масштабным: заявки отправили более 2,7 тысяч студентов, до финала дошли 56 команд, из которых были выбраны победители. В VK Education отмечают, что участники продемонстрировали отличное понимание реальных потребностей пользователей и вузов и создали актуальные и востребованные мини-приложения и чат-боты для мессенджера МАХ.
@algoses
Хакатон получился масштабным: заявки отправили более 2,7 тысяч студентов, до финала дошли 56 команд, из которых были выбраны победители. В VK Education отмечают, что участники продемонстрировали отличное понимание реальных потребностей пользователей и вузов и создали актуальные и востребованные мини-приложения и чат-боты для мессенджера МАХ.
@algoses
🤣88💊8🤨7❤3🤯2
Задача с собеседования в Яндекс
Дан массив строк strs. Найдите анаграммы и сгруппируйте их вместе.
Группы анаграмм можно вывести в любом порядке.
Пример:
Input: strs = ["eat","tea","tan","ate","nat","bat"]
Output: [["bat"],["nat","tan"],["ate","eat","tea"]]
Чат алгоритмистов
Решение
Идея: хэш-таблица, где ключ - кортеж из 26 эл-ов (каждое число - количество вхождений определённой буквы в слово), а значение - список слов, имеющих одинаковую частоту букв (анаграммы).
Проходим по каждому слову в массиве strs и создаём массив из 26 нулей (кол-во букв алфавита). Для каждого символа в слове вычисляем его индекс (соответствует позиции буквы в алфавите) и увеличиваем счётчик.
Далее преобразовываем список в кортеж, чтобы сделать его ключом словаря.
Проверяем, есть ли ключ в словаре. Если да: добавляем слово в существующий список анаграмм по этому ключу, если нет: создаём новую группу анаграмм.
Выводим двумерный список, состоящий из сгруппированных значений нашего словаря с анаграммами.
Сложность
O(n × k) - по времени
O(n × k) - по памяти
Код
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
anagrams = {}
for word in strs:
letter_counts = [0] * 26
for char in word:
letter_counts[ord(char) - ord("a")] += 1
key = tuple(letter_counts)
if key not in anagrams:
anagrams[key] = [word]
else:
anagrams[key].append(word)
return list(anagrams.values())
@algoses
Дан массив строк strs. Найдите анаграммы и сгруппируйте их вместе.
Группы анаграмм можно вывести в любом порядке.
Пример:
Input: strs = ["eat","tea","tan","ate","nat","bat"]
Output: [["bat"],["nat","tan"],["ate","eat","tea"]]
Чат алгоритмистов
Решение
Проходим по каждому слову в массиве strs и создаём массив из 26 нулей (кол-во букв алфавита). Для каждого символа в слове вычисляем его индекс (соответствует позиции буквы в алфавите) и увеличиваем счётчик.
Далее преобразовываем список в кортеж, чтобы сделать его ключом словаря.
Проверяем, есть ли ключ в словаре. Если да: добавляем слово в существующий список анаграмм по этому ключу, если нет: создаём новую группу анаграмм.
Выводим двумерный список, состоящий из сгруппированных значений нашего словаря с анаграммами.
Сложность
O(n × k) - по времени
O(n × k) - по памяти
Код
anagrams = {}
for word in strs:
letter_counts = [0] * 26
for char in word:
letter_counts[ord(char) - ord("a")] += 1
key = tuple(letter_counts)
if key not in anagrams:
anagrams[key] = [word]
else:
anagrams[key].append(word)
return list(anagrams.values())
@algoses
🔥9❤8👏2
Задача с собеседования в Zalando
В лимонадном киоске один стакан лимонада стоит 5 долларов. Покупатели стоят в очереди и приобретают лимонад в порядке, указанном в массиве bills. Каждый клиент покупает только один лимонад и платит одной из следующих купюр: 5, 10 или 20 долларов.
Вам необходимо дать правильную сдачу каждому клиенту, чтобы итоговая сумма, которую он заплатил, составила ровно 5 долларов. Имейте в виду, что вначале у вас нет денег для сдачи.
Задан целочисленный массив bills, где bills[i] - это купюра, которой расплачивается i-й любитель лимонада.
Верните true, если можете дать правильную сдачу каждому клиенту, или false в противном случае.
Пример 1:
Input: bills = [5,5,5,10,20]
Output: true
Пример 2:
Input: bills = [5,5,10,10,20]
Output: false
Чат алгоритмистов
Решение
Создаём переменные для хранения купюр, переменная twenty нам не нужна, потому что не будет использоваться в качестве сдачи.
Проходим по каждому элементу в массиве и обрабатываем платежи, увеличивая (принимая купюру) и/или уменьшая (при необходимости сдачи) счётчик переменной.
Проверяем возможность дать клиенту сдачу при каждом платеже, применяя жадный алгоритм. Ищем наиболее оптимальное решение на каждом шаге:
При оплате 10$ - выбора нет, отдаём купюру номиналом 5, и это наиболее оптимально в данном случае. Но если клиент платит 20$, предпочитаем дать сдачу с использованием купюры наибольшего номинала (10+5, а не 5+5+5), так как 5$ более универсальны и чаще могут понадобиться для размена.
Сложность
O(n) - по времени
O(1) - по памяти
Код
class Solution:
def lemonadeChange(self, bills: List[int]) -> bool:
five = 0
ten = 0
for b in bills:
if b == 5:
five += 1
elif b == 10:
if five < 1:
return False
else:
five -= 1
ten += 1
else:
if five >= 1 and ten >= 1:
ten -= 1
five -= 1
elif five >= 3:
five -= 3
else:
return False
return True
@algoses
В лимонадном киоске один стакан лимонада стоит 5 долларов. Покупатели стоят в очереди и приобретают лимонад в порядке, указанном в массиве bills. Каждый клиент покупает только один лимонад и платит одной из следующих купюр: 5, 10 или 20 долларов.
Вам необходимо дать правильную сдачу каждому клиенту, чтобы итоговая сумма, которую он заплатил, составила ровно 5 долларов. Имейте в виду, что вначале у вас нет денег для сдачи.
Задан целочисленный массив bills, где bills[i] - это купюра, которой расплачивается i-й любитель лимонада.
Верните true, если можете дать правильную сдачу каждому клиенту, или false в противном случае.
Пример 1:
Input: bills = [5,5,5,10,20]
Output: true
Пример 2:
Input: bills = [5,5,10,10,20]
Output: false
Чат алгоритмистов
Решение
Проходим по каждому элементу в массиве и обрабатываем платежи, увеличивая (принимая купюру) и/или уменьшая (при необходимости сдачи) счётчик переменной.
Проверяем возможность дать клиенту сдачу при каждом платеже, применяя жадный алгоритм. Ищем наиболее оптимальное решение на каждом шаге:
При оплате 10$ - выбора нет, отдаём купюру номиналом 5, и это наиболее оптимально в данном случае. Но если клиент платит 20$, предпочитаем дать сдачу с использованием купюры наибольшего номинала (10+5, а не 5+5+5), так как 5$ более универсальны и чаще могут понадобиться для размена.
O(n) - по времени
O(1) - по памяти
Код
class Solution:
def lemonadeChange(self, bills: List[int]) -> bool:
five = 0
ten = 0
for b in bills:
if b == 5:
five += 1
elif b == 10:
if five < 1:
return False
else:
five -= 1
ten += 1
else:
if five >= 1 and ten >= 1:
ten -= 1
five -= 1
elif five >= 3:
five -= 3
else:
return False
return True
@algoses
❤6🔥5👏2
Задача с собеседования в Zalando
Дан массив интервалов, где intervals[i] = [starti, endi]. Необходимо объединить все пересекающиеся интервалы, а затем вернуть массив, состоящий из непересекающихся интервалов, которые покрывают все интервалы из входных данных.
Пример 1:
Input: intervals = [[1,3],[2,6],[8,10],[15,18]]
Output: [[1,6],[8,10],[15,18]]
Пример 2:
Input: intervals = [[4,7],[1,4]]
Output: [[1,7]]
Чат алгоритмистов
Решение
1. Сортируем интервалы по началу, это гарантирует: если текущий интервал не пересекается с последним, то он не может пересекаться с каким-либо более ранним, т.к. все предыдущие интервалы имеют начало не больше, чем у последнего, и если последний заканчивается до начала текущего, то все предыдущие тем более закончились раньше. Это позволяет решить задачу за один проход.
2. Проходим по каждому отсортированному интервалу в массиве:
- добавляем новый интервал в список, если это первый интервал в списке ИЛИ если конец последнего интервала меньше начала текущего
(в merged лежит [1,6], а текущий интервал [8,10], 6 < 8 => интервалы не пересекаются => добавляем [8,10] как новый интервал).
- иначе объединяем его с последним интервалом и обновляем конец
(в merged лежит [1,3], текущий интервал = [2,6], они пересекающиеся, так как 3 ≥ 2. Обновляем конец объединённого интервала: max(3, 6) = 6, то есть [1,6]).
Сложность
O(n log n) - по времени
O(n) - по памяти
Код
class Solution:
def merge(self, intervals: List[List[int]]) -> List[List[int]]:
intervals.sort(key = lambda interval: interval[0])
merged = []
for interval in intervals:
if not merged or merged[-1][1] < interval[0]:
merged.append(interval)
else:
merged[-1][1] = max(merged[-1][1], interval[1])
return merged
@algoses
Дан массив интервалов, где intervals[i] = [starti, endi]. Необходимо объединить все пересекающиеся интервалы, а затем вернуть массив, состоящий из непересекающихся интервалов, которые покрывают все интервалы из входных данных.
Пример 1:
Input: intervals = [[1,3],[2,6],[8,10],[15,18]]
Output: [[1,6],[8,10],[15,18]]
Пример 2:
Input: intervals = [[4,7],[1,4]]
Output: [[1,7]]
Чат алгоритмистов
Решение
2. Проходим по каждому отсортированному интервалу в массиве:
- добавляем новый интервал в список, если это первый интервал в списке ИЛИ если конец последнего интервала меньше начала текущего
(в merged лежит [1,6], а текущий интервал [8,10], 6 < 8 => интервалы не пересекаются => добавляем [8,10] как новый интервал).
- иначе объединяем его с последним интервалом и обновляем конец
(в merged лежит [1,3], текущий интервал = [2,6], они пересекающиеся, так как 3 ≥ 2. Обновляем конец объединённого интервала: max(3, 6) = 6, то есть [1,6]).
Сложность
O(n) - по памяти
Код
def merge(self, intervals: List[List[int]]) -> List[List[int]]:
intervals.sort(key = lambda interval: interval[0])
merged = []
for interval in intervals:
if not merged or merged[-1][1] < interval[0]:
merged.append(interval)
else:
merged[-1][1] = max(merged[-1][1], interval[1])
return merged
@algoses
❤6🔥2👏1
Задача с собеседования в Zalando
Даны три целых числа: x, y и z.
У вас есть x строк, равных "AA", y строк, равных "BB", и z строк, равных "AB". Вы хотите выбрать некоторое (возможно, все или ни одной) количество из этих строк и объединить их в некотором порядке, чтобы сформировать новую строку. Эта новая строка не должна содержать подстрок "AAA" или "BBB".
Известно, что подстрока - это непрерывная непустая последовательность символов внутри строки.
Верните максимально возможную длину новой строки.
Пример 1:
Input: x = 2, y = 5, z = 1
Output: 12
Объяснение: мы можем объединить строки "BB", "AA", "BB", "AA", "BB" и "AB" в таком порядке. Тогда наша новая строка будет "BBAABBAABBAB".
Длина этой строки равна 12, и мы можем показать, что невозможно сформировать строку большей длины.
Пример 2:
Input: x = 3, y = 2, z = 2
Output: 14
Объяснение: мы можем объединить строки "AB", "AB", "AA", "BB", "AA", "BB" и "AA" в таком порядке. Тогда наша новая строка будет "ABABAABBAABBAA".
Длина этой строки равна 14, и мы можем показать, что невозможно сформировать строку большей длины.
Ограничения:
1 <= x, y, z <= 50
НАШ ЧАТ АЛГОРИТМИСТОВ
Решение
Задачу можно решить с помощью жадного алгоритма или более элегантно - с помощью выведения математической формулы, считающей длину строки. Используем второй способ:
Последовательности, которые мы можем соединять:
"AB" + "AA" = "ABAA"
"BB" + "AB" = "BBAB"
"AA" + "BB" = "AABB"
И те, которые НЕ можем:
"AA" + "AA" = "AAAA"
"BB" + "BB" = "BBBB"
"AA" + "AB" = "AAAB"
Строка "AB" самая безопасная и может использоваться для разбиения других последовательностей.
Если у нас равное количество строк "AA" и "BB" (x == y), то, чтобы высчитать максимальную длину строки нужно: сложить количество строк "AA", "BB" и "AB" и умножить сумму на 2 (по 2 символа в каждой строке).
Если же количество строк "AA" и "BB" различно, то: чередуем "AA" и "BB" столько раз, сколько можно. Далее, когда один из типов строк заканчивается, добавляем ещё одну строку противоположного типа. Так мы максимально продляем строку и не допускаем образования запрещённой подстроки.
Для наглядности:
Если x < y: ...AA + BB + AB - и дальше можем продолжать строку только типом "AB".
Если x > y: ...BB + AA или ...BB + AB + AA - и далее строку продолжать невозможно.
Определим, строк какого типа у нас меньше ("AA" или "BB"). Сформулируем формулу: мы используем все строки безопасного типа "AB" (по 2 символа в каждой: z * 2), также все строки того типа, которого у нас меньше (m * 2 символа), и строки противоположного типа в количестве, равном количеству строк менее частого типа + 1 ((m + 1)* 2 символа).
Сложность
O(1) - по времени
O(1) - по памяти
Код
class Solution:
def longestString(self, x: int, y: int, z: int) -> int:
m = min(x, y)
if x == y:
return 2 * (x + y + z)
else:
return 2 * m + (m + 1) * 2 + 2 * z
@algoses
Даны три целых числа: x, y и z.
У вас есть x строк, равных "AA", y строк, равных "BB", и z строк, равных "AB". Вы хотите выбрать некоторое (возможно, все или ни одной) количество из этих строк и объединить их в некотором порядке, чтобы сформировать новую строку. Эта новая строка не должна содержать подстрок "AAA" или "BBB".
Известно, что подстрока - это непрерывная непустая последовательность символов внутри строки.
Верните максимально возможную длину новой строки.
Пример 1:
Input: x = 2, y = 5, z = 1
Output: 12
Объяснение: мы можем объединить строки "BB", "AA", "BB", "AA", "BB" и "AB" в таком порядке. Тогда наша новая строка будет "BBAABBAABBAB".
Длина этой строки равна 12, и мы можем показать, что невозможно сформировать строку большей длины.
Пример 2:
Input: x = 3, y = 2, z = 2
Output: 14
Объяснение: мы можем объединить строки "AB", "AB", "AA", "BB", "AA", "BB" и "AA" в таком порядке. Тогда наша новая строка будет "ABABAABBAABBAA".
Длина этой строки равна 14, и мы можем показать, что невозможно сформировать строку большей длины.
Ограничения:
1 <= x, y, z <= 50
НАШ ЧАТ АЛГОРИТМИСТОВ
Решение
Последовательности, которые мы можем соединять:
"AB" + "AA" = "ABAA"
"BB" + "AB" = "BBAB"
"AA" + "BB" = "AABB"
И те, которые НЕ можем:
"AA" + "AA" = "AAAA"
"BB" + "BB" = "BBBB"
"AA" + "AB" = "AAAB"
Строка "AB" самая безопасная и может использоваться для разбиения других последовательностей.
Если у нас равное количество строк "AA" и "BB" (x == y), то, чтобы высчитать максимальную длину строки нужно: сложить количество строк "AA", "BB" и "AB" и умножить сумму на 2 (по 2 символа в каждой строке).
Если же количество строк "AA" и "BB" различно, то: чередуем "AA" и "BB" столько раз, сколько можно. Далее, когда один из типов строк заканчивается, добавляем ещё одну строку противоположного типа. Так мы максимально продляем строку и не допускаем образования запрещённой подстроки.
Для наглядности:
Если x < y: ...AA + BB + AB - и дальше можем продолжать строку только типом "AB".
Если x > y: ...BB + AA или ...BB + AB + AA - и далее строку продолжать невозможно.
Определим, строк какого типа у нас меньше ("AA" или "BB"). Сформулируем формулу: мы используем все строки безопасного типа "AB" (по 2 символа в каждой: z * 2), также все строки того типа, которого у нас меньше (m * 2 символа), и строки противоположного типа в количестве, равном количеству строк менее частого типа + 1 ((m + 1)* 2 символа).
Сложность
O(1) - по памяти
Код
def longestString(self, x: int, y: int, z: int) -> int:
m = min(x, y)
if x == y:
return 2 * (x + y + z)
else:
return 2 * m + (m + 1) * 2 + 2 * z
@algoses
🔥4❤1👏1
Forwarded from Поступашки - ШАД, Стажировки и Магистратура
Новогодние скидки на нашу линейку математических курсов 🎓
Хочешь поступить в ШАД, Ai Masters, или ААА? А может ты мечтаешь тащить собесы и поступить в крутую магу, но тебе не хватает фундамента? Узнал себя? Тогда записывайся у администратора на любой из курсов:
➡️ алгоритмы
➡️ теория вероятностей
➡️ линейная алгебра
➡️ математический анализ
Наши курсы заточены на практику и конкретные задачи, вся теория будет разобрана на конкретных задачах и примерах, которые будут на экзаменах и на собесах. Ничего нудного и скучного! Изучаем только то, что вам реально понадобится! Хочешь конкретики? На нашам сайте можно найти программу, подробности и отзывы на каждый курс.
Помимо кучи авторских задач мы даем доступ к уникальной закрытой базе заданий ШАДа, разбор реального контеста в ШАД, разбор ВСЕХ задач с собеседований в ШАД, Ai Masters, ААА! Более того, вы получите эксклюзивные материалы для проверяющих с собесов, пробный экзамен, инсайды, персональные рекомендации, собес с подробной консультацией и дальнейшим сопровождением вплоть до поступления в место мечты! А если вы выполните все рекомендации, но не достигнете поставленной цели, то вам вернут все потраченные деньги!
Для вопросов и покупок пишем администратору и не тянем с этим: на каждом курсе количество мест ограничено!
Хочешь поступить в ШАД, Ai Masters, или ААА? А может ты мечтаешь тащить собесы и поступить в крутую магу, но тебе не хватает фундамента? Узнал себя? Тогда записывайся у администратора на любой из курсов:
Наши курсы заточены на практику и конкретные задачи, вся теория будет разобрана на конкретных задачах и примерах, которые будут на экзаменах и на собесах. Ничего нудного и скучного! Изучаем только то, что вам реально понадобится! Хочешь конкретики? На нашам сайте можно найти программу, подробности и отзывы на каждый курс.
Помимо кучи авторских задач мы даем доступ к уникальной закрытой базе заданий ШАДа, разбор реального контеста в ШАД, разбор ВСЕХ задач с собеседований в ШАД, Ai Masters, ААА! Более того, вы получите эксклюзивные материалы для проверяющих с собесов, пробный экзамен, инсайды, персональные рекомендации, собес с подробной консультацией и дальнейшим сопровождением вплоть до поступления в место мечты! А если вы выполните все рекомендации, но не достигнете поставленной цели, то вам вернут все потраченные деньги!
Для вопросов и покупок пишем администратору и не тянем с этим: на каждом курсе количество мест ограничено!
Please open Telegram to view this post
VIEW IN TELEGRAM
❤3
Задача с собеседования в Zalando
Даны два целых числа a и b. Верните любую возможную строку s, такую, что:
- s имеет длину, равную a + b, и содержит ровно a букв "a" и ровно b букв "b",
- подстрока "aaa" не встречается в s,
- и подстрока "bbb" не содержится в s.
Пример 1:
Input: a = 1, b = 2
Output: "abb"
("abb", "bab" and "bba" - корректные варианты)
Пример 2:
Input: a = 4, b = 1
Output: "aabaa"
Ограничения:
0 <= a, b <= 100
Гарантируется, что существует строка s для данных a и b.
НАШ ЧАТ АЛГОРИТМИСТОВ
Решение
Применяем жадный алгоритм: выбираем символ, которого у нас больше, чтобы сбалансировать разницу между ними и сохранить менее частый символ для случаев, когда необходимо будет разбить последовательность более частого. Если два последних символа одинаковы - добавляем противоположный, чтобы не допустить подстроки с тремя повторяющимися буквами.
Сложность
O(a + b) - по времени
O(a + b) - по памяти
Код
class Solution:
def strWithout3a3b(self, a: int, b: int) -> str:
result = []
while a > 0 or b > 0:
if len(result) >=2 and result[-1] == result[-2]:
if result[-1] == "a":
result.append("b")
b -= 1
else:
result.append("a")
a -= 1
else:
if a >= b:
result.append("a")
a -= 1
else:
result.append("b")
b -= 1
return "".join(result)
@algoses
Даны два целых числа a и b. Верните любую возможную строку s, такую, что:
- s имеет длину, равную a + b, и содержит ровно a букв "a" и ровно b букв "b",
- подстрока "aaa" не встречается в s,
- и подстрока "bbb" не содержится в s.
Пример 1:
Input: a = 1, b = 2
Output: "abb"
("abb", "bab" and "bba" - корректные варианты)
Пример 2:
Input: a = 4, b = 1
Output: "aabaa"
Ограничения:
0 <= a, b <= 100
Гарантируется, что существует строка s для данных a и b.
НАШ ЧАТ АЛГОРИТМИСТОВ
Решение
Сложность
O(a + b) - по памяти
Код
def strWithout3a3b(self, a: int, b: int) -> str:
result = []
while a > 0 or b > 0:
if len(result) >=2 and result[-1] == result[-2]:
if result[-1] == "a":
result.append("b")
b -= 1
else:
result.append("a")
a -= 1
else:
if a >= b:
result.append("a")
a -= 1
else:
result.append("b")
b -= 1
return "".join(result)
@algoses
❤4🔥2👏1
Задача с собеседования в Zenefits
Дан массив nums размером n, верните элемент большинства (гарантируется, что он всегда есть в массиве).
Элемент большинства (или мажоритарный) - это преобладающий элемент последовательности, который в данном случае должен встречаться более, чем n / 2 раз.
Решите задачу за линейное время и с O(1) по памяти.
Пример 1:
Input: nums = [3,2,3]
Output: 3
Пример 2:
Input: nums = [2,2,1,1,1,2,2]
Output: 2
НАШ ЧАТ АЛГОРИТМИСТОВ
Решение
Без дополнительного условия на асимптотику задачу можно было бы решить с использованием хэш-таблицы, где ключ - элемент массива, а значение - кол-во его вхождений в массив. Сложность при этом: O(n) - по времени и O(n) - по памяти.
С учётом условия улучшения пространственной сложности до O(1), оптимальным и стандартным будет использование алгоритма Бойера-Мура:
Создаём две переменные: result (содержит элемент, претендующий на то, чтобы оказаться мажоритарным) и count (счётчик "приоритета" элемента-кандидата на преобладание в последовательности). В начале цикла кандидатом окажется элемент, идущий первым в массиве, увеличиваем счётчик его "приоритета" на 1. Далее мы сравниваем каждый последующий элемент с текущим значением result, если они равны - увеличиваем счётчик, если нет - уменьшаем. Если в какой-то момент счётчик приоритета становится равным 0, выбираем нового кандидата и записываем в result. Так проходим по всем эл-там массива и в конце выводим result с последним вписанным в него значением.
Таким образом, суть алгоритма в симуляции попарного "обнуления" противоположных элементов путём уменьшения счётчика count. Так как преобладающий эл-т составляет больше половины всей последовательности, в процессе для него не останется противоположного элемента для попарного "удаления". Наличие мажоритарного эл-та гарантируется условием задачи, поэтому нам не нужна дополнительная проверка.
Сложность
O(n) - по времени
O(1) - по памяти
Код
class Solution:
def majorityElement(self, nums: List[int]) -> int:
result, count = 0, 0
for i in nums:
if count == 0:
result = i
count += 1
elif i == result:
count += 1
else:
count -= 1
return result
@algoses
Дан массив nums размером n, верните элемент большинства (гарантируется, что он всегда есть в массиве).
Элемент большинства (или мажоритарный) - это преобладающий элемент последовательности, который в данном случае должен встречаться более, чем n / 2 раз.
Решите задачу за линейное время и с O(1) по памяти.
Пример 1:
Input: nums = [3,2,3]
Output: 3
Пример 2:
Input: nums = [2,2,1,1,1,2,2]
Output: 2
НАШ ЧАТ АЛГОРИТМИСТОВ
Решение
С учётом условия улучшения пространственной сложности до O(1), оптимальным и стандартным будет использование алгоритма Бойера-Мура:
Создаём две переменные: result (содержит элемент, претендующий на то, чтобы оказаться мажоритарным) и count (счётчик "приоритета" элемента-кандидата на преобладание в последовательности). В начале цикла кандидатом окажется элемент, идущий первым в массиве, увеличиваем счётчик его "приоритета" на 1. Далее мы сравниваем каждый последующий элемент с текущим значением result, если они равны - увеличиваем счётчик, если нет - уменьшаем. Если в какой-то момент счётчик приоритета становится равным 0, выбираем нового кандидата и записываем в result. Так проходим по всем эл-там массива и в конце выводим result с последним вписанным в него значением.
Таким образом, суть алгоритма в симуляции попарного "обнуления" противоположных элементов путём уменьшения счётчика count. Так как преобладающий эл-т составляет больше половины всей последовательности, в процессе для него не останется противоположного элемента для попарного "удаления".
Сложность
O(1) - по памяти
Код
def majorityElement(self, nums: List[int]) -> int:
result, count = 0, 0
for i in nums:
if count == 0:
result = i
count += 1
elif i == result:
count += 1
else:
count -= 1
return result
@algoses
❤17😈6🤯3💅1
Т-академия
Экзамены до 22 января. Курсы для фаст трека на стажировку. Податься одновременно и на стажировку и на академию нельзя, только если с фейков. Задания уже выложены тут. Разбор алгоритмов для аналитиков и бэкендеров будет только на нашем курсе по алгоритмам, на который скидка заканчивается уже сегодня.
Экзамены до 22 января. Курсы для фаст трека на стажировку. Податься одновременно и на стажировку и на академию нельзя, только если с фейков. Задания уже выложены тут. Разбор алгоритмов для аналитиков и бэкендеров будет только на нашем курсе по алгоритмам, на который скидка заканчивается уже сегодня.
❤3
Forwarded from Поступашки - ШАД, Стажировки и Магистратура
Первые впечатления: в этом году сами задачи выглядят сложнее, условия сформулированы неочевидно, а нейросети плохо их интерпретируют и дают неверные решения, поэтому стратегия "тупо решу через ГПТ" в этом году 100% не сработает.
Поэтому мы сделали подробнейший разбор заданий контеста Т-банка на наших курсах. Да, покупая их, помимо разбора, ты получаешь еще и:
🔽 Разбор актуальных контестов Яндекса🔽 Огромный банк реальных технических вопросов🔽 Записи реальных собесов и интервью🔽 Реальные тестовые задания топовых бигтехов🔽 Разбор всех задач с алгсобесов Яндекса
Please open Telegram to view this post
VIEW IN TELEGRAM
❤3
Задача с собеседования в Zenefits
Дана двумерная бинарная матрица grid размером m x n, которая представляет карту из единиц (суша) и нулей (вода). Верните количество островов.
Остров окружён водой и сформирован путём соединения соседних участков суши (ячеек "1") по горизонтали или вертикали. Можете считать, что все четыре края сетки окружены водой.
Пример 1:
Input: grid = [
["1","1","1","1","0"],
["1","1","0","1","0"],
["1","1","0","0","0"],
["0","0","0","0","0"]
]
Output: 1
Пример 2:
Input: grid = [
["1","1","0","0","0"],
["1","1","0","0","0"],
["0","0","1","0","0"],
["0","0","0","1","1"]
]
Output: 3
Ограничения:
m == grid.length
n == grid[i].length
1 <= m, n <= 300
grid[i][j] is '0' or '1'.
НАШ ЧАТ АЛГОРИТМИСТОВ
Решение
Задачу можно решить рекурсивным обходом графа в глубину (dfs), так как ограничения размера 300×300 позволяют полагать, что опасности переполнения стека нет.
Вершины графа - ячейки с "1".
Рёбра - соседние ячейки по горизонтали и вертикали.
В цикле проходим по каждой ячейке матрицы, если ячейка содержит "1": увеличиваем количество островов и запускаем рекурсивную функцию dfs. В ней проверяем ячейку на соблюдение границ и помечаем этот участок суши, как пройденный (grid[i][j] = "0"). Следующими вызовами проверяем её соседей по горизонтали и вертикали.
На больших данных оптимальным будет решение через bfs или итеративный dfs, но без условия оптимизации рекурсивное dfs-решение стандартно для этой задачи.
Сложность
O(m*n) - по времени
O(m*n) - по памяти
Код
class Solution:
def numIslands(self, grid: List[List[str]]) -> int:
m, n = len(grid), len(grid[0])
num_islands = 0
def dfs(i, j):
if i < 0 or i >= m or j < 0 or j >= n or grid[i][j] != "1":
return
else:
grid[i][j] = "0"
dfs(i, j+1)
dfs(i+1, j)
dfs(i, j-1)
dfs(i-1, j)
for i in range(m):
for j in range(n):
if grid[i][j] == "1":
num_islands += 1
dfs(i, j)
return num_islands
@algoses
Дана двумерная бинарная матрица grid размером m x n, которая представляет карту из единиц (суша) и нулей (вода). Верните количество островов.
Остров окружён водой и сформирован путём соединения соседних участков суши (ячеек "1") по горизонтали или вертикали. Можете считать, что все четыре края сетки окружены водой.
Пример 1:
Input: grid = [
["1","1","1","1","0"],
["1","1","0","1","0"],
["1","1","0","0","0"],
["0","0","0","0","0"]
]
Output: 1
Пример 2:
Input: grid = [
["1","1","0","0","0"],
["1","1","0","0","0"],
["0","0","1","0","0"],
["0","0","0","1","1"]
]
Output: 3
Ограничения:
m == grid.length
n == grid[i].length
1 <= m, n <= 300
grid[i][j] is '0' or '1'.
НАШ ЧАТ АЛГОРИТМИСТОВ
Решение
Вершины графа - ячейки с "1".
Рёбра - соседние ячейки по горизонтали и вертикали.
В цикле проходим по каждой ячейке матрицы, если ячейка содержит "1": увеличиваем количество островов и запускаем рекурсивную функцию dfs. В ней проверяем ячейку на соблюдение границ и помечаем этот участок суши, как пройденный (grid[i][j] = "0"). Следующими вызовами проверяем её соседей по горизонтали и вертикали.
На больших данных оптимальным будет решение через bfs или итеративный dfs, но без условия оптимизации рекурсивное dfs-решение стандартно для этой задачи.
Сложность
O(m*n) - по памяти
Код
def numIslands(self, grid: List[List[str]]) -> int:
m, n = len(grid), len(grid[0])
num_islands = 0
def dfs(i, j):
if i < 0 or i >= m or j < 0 or j >= n or grid[i][j] != "1":
return
else:
grid[i][j] = "0"
dfs(i, j+1)
dfs(i+1, j)
dfs(i, j-1)
dfs(i-1, j)
for i in range(m):
for j in range(n):
if grid[i][j] == "1":
num_islands += 1
dfs(i, j)
return num_islands
@algoses
❤4👍1
Задача с собеседования в Zoox
Дан массив prices, где prices[i] - это цена акции в i-й день.
Вы хотите максимизировать прибыль, выбрав один день для покупки акции и другой день в будущем для её продажи.
Верните максимальную прибыль, которую вы можете получить с этой сделки. Если прибыль получить невозможно, верните 0.
Пример 1:
Input: prices = [7,1,5,3,6,4]
Output: 5
Объяснение: Покупаете акцию во 2-й день (цена = 1) и продаёте в 5-й (цена = 6). Прибыль: 6 - 1 = 5. Обратите внимание, что покупка во 2-й день и продажа в 1-й невозможна, так как вы должны сначала купить, а потом продать!
Пример 2:
Input: prices = [7,6,4,3,1]
Output: 0
Объяснение: В этом случае сделок не совершается (нет выгодных условий), и максимальная прибыль равно 0.
НАШ ЧАТ АЛГОРИТМИСТОВ
Решение
Для получения максимальной прибыли - покупаем на минимуме и продаём на максимуме. Заводим две переменные: min_price (находим самую выгодную цену) и max_profit (ищем наибольшую разницу между минимумом и текущей стоимостью акции).
Осуществляем проход по массиву и сравниваем минимальную цену (вначале она равняется первому эл-ту массива) с текущей. Если текущая меньше минимальной, обновляем значение минимума. Последний найденный минимум - цена, по которой мы купим акцию. Далее, в независимости от того, выполнилось ли условие для обновления минимума, проверяем: будет ли максимальной прибыль от продажи акции в текущий день. Сравниваем ранее найденное значение max_profit с прибылью, которую получим от продажи акции "сегодня". После окончания работы цикла выводим max_profit с последним вписанным в переменную значением.
Задача помечена тегом "Dynamic Programming", подход использован в решении: мы храним две переменные (min_price, max_profit) и используем их найденные значения для вычисления следующих.
Сложность
O(n) - по времени
O(1) - по памяти
Код
class Solution:
def maxProfit(self, prices: List[int]) -> int:
if not prices:
return 0
min_price = prices[0]
max_profit = 0
for price in prices:
if price < min_price:
min_price = price
max_profit = max(max_profit, price - min_price)
return max_profit
@algoses
Дан массив prices, где prices[i] - это цена акции в i-й день.
Вы хотите максимизировать прибыль, выбрав один день для покупки акции и другой день в будущем для её продажи.
Верните максимальную прибыль, которую вы можете получить с этой сделки. Если прибыль получить невозможно, верните 0.
Пример 1:
Input: prices = [7,1,5,3,6,4]
Output: 5
Объяснение: Покупаете акцию во 2-й день (цена = 1) и продаёте в 5-й (цена = 6). Прибыль: 6 - 1 = 5. Обратите внимание, что покупка во 2-й день и продажа в 1-й невозможна, так как вы должны сначала купить, а потом продать!
Пример 2:
Input: prices = [7,6,4,3,1]
Output: 0
Объяснение: В этом случае сделок не совершается (нет выгодных условий), и максимальная прибыль равно 0.
НАШ ЧАТ АЛГОРИТМИСТОВ
Решение
Осуществляем проход по массиву и сравниваем минимальную цену (вначале она равняется первому эл-ту массива) с текущей. Если текущая меньше минимальной, обновляем значение минимума. Последний найденный минимум - цена, по которой мы купим акцию. Далее, в независимости от того, выполнилось ли условие для обновления минимума, проверяем: будет ли максимальной прибыль от продажи акции в текущий день. Сравниваем ранее найденное значение max_profit с прибылью, которую получим от продажи акции "сегодня". После окончания работы цикла выводим max_profit с последним вписанным в переменную значением.
Задача помечена тегом "Dynamic Programming", подход использован в решении: мы храним две переменные (min_price, max_profit) и используем их найденные значения для вычисления следующих.
Сложность
O(1) - по памяти
Код
def maxProfit(self, prices: List[int]) -> int:
if not prices:
return 0
min_price = prices[0]
max_profit = 0
for price in prices:
if price < min_price:
min_price = price
max_profit = max(max_profit, price - min_price)
return max_profit
@algoses
❤9🔥2😈2
Media is too big
VIEW IN TELEGRAM
Разбор алгоритмов Т-банк (первая часть)
Разбор второй части и всех направлений выложен только на наших курсах. До конца дня действует льготная цена13450 8950. Записываемся осталось 6 часов до конца экзамена!
@algoses
Разбор второй части и всех направлений выложен только на наших курсах. До конца дня действует льготная цена
@algoses
🔥8❤2
Задача с собеседования в eBay
Перед вами замок с 4 вращающимися дисками. Каждый диск имеет 10 слотов: '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'. Диски можно свободно вращать вперёд и назад: например, вы можете превратить '9' в '0' или '0' в '9'. Каждый ход - это поворот одного диска на одну позицию.
Замок начинает с комбинации '0000' (строка, представляющая состояние 4-х дисков).
Вам дан список тупиков - deadends: если на замке отображается какая-либо из запрещённых комбинаций, диски замка перестают вращаться, и вы не можете его открыть.
Вам также дана целевая комбинация target, представляющая значение дисков, при котором замок откроется. Верните минимальное возможное количество вращений, необходимое для открытия замка. Если это невозможно, верните -1.
Пример 1:
Input: deadends = ["0201","0101","0102","1212","2002"], target = "0202"
Output: 6
Объяснение: последовательность допустимых ходов может быть такой: "0000" -> "1000" -> "1100" -> "1200" -> "1201" -> "1202" -> "0202". Обратите внимание, что последовательность типа "0000" -> "0001" -> "0002" -> "0102" -> "0202" будет недопустимой, так как диски замка остановятся после запрещённой комбинации "0102".
Пример 2:
Input: deadends = ["8888"], target = "0009"
Output: 1
Объяснение: мы можем повернуть последний диск в обратном направлении, чтобы перейти от "0000" к "0009".
Пример 3:
Input: deadends = ["8887","8889","8878","8898","8788","8988","7888","9888"], target = "8888"
Output: -1
Объяснение: мы не можем достичь цели, не попав в тупик.
Ограничения:
1 <= deadends.length <= 500
deadends[i].length == 4
target.length == 4
target не может присутствовать в списке deadends
target и deadends[i] состоят только из цифр
НАШ ЧАТ АЛГОРИТМИСТОВ
Решение
Рассмотрим задачу как поиск кратчайшего пути в графе.
Вершины графа - комбинации цифр на 4 дисках, каждый диск - одна позиция в комбинации. Некоторые вершины (deadends) заблокированы, мы не можем посещать их на пути к целевой вершине (target). Граф - невзвешенный, так как все рёбра имеют одинаковый вес (один поворот диска) => используем алгоритм поиска в ширину (BFS).
Начинаем обход с начального состояния '0000' (если оно в списке deadends - до target мы не дойдём, возвращаем -1), добавляем в очередь в виде кортежа (комбинация цифр и количество вращений). Все запрещённые комбинации сразу добавляем во множество visited, чтобы избежать попадания в тупик.
Пока очередь не пуста: извлекаем текущую комбинацию и количество вращений. Если комбинация соответствует target - возвращаем число вращений. Иначе: генерируем 8 следующих комбинаций, увеличивая и уменьшая каждую из 4 цифр (2 направления: 1 поворот диска вперёд и назад). Чтобы реализовать закольцованность дисков: деление по модулю (%10), "9" -> "0" при вращении диска вперёд ((9 + 1) % 10 = 0) и "0" -> "9" при вращении назад ((0 - 1) % 10 = 9).
Проверяем комбинацию: если ещё не посещена - добавляем в visited и помещаем в очередь, увеличивая количество вращений на 1.
Если очередь опустела, а target не достигнута - пути не существует, возвращаем -1.
Сложность
O(V + E) - по времени
O(V) - по памяти
Код
class Solution:
def openLock(self, deadends: List[str], target: str) -> int:
if "0000" in deadends:
return -1
def new_combinations(node):
result = []
for i in range(4):
next_digit = (int(node[i]) + 1) % 10
result.append(node[:i] + str(next_digit) + node[i + 1:])
prev_digit = (int(node[i]) - 1) % 10
result.append(node[:i] + str(prev_digit) + node[i + 1:])
return result
q = deque([("0000", 0)])
visited = set(deadends)
visited.add("0000")
while q:
node, turns = q.popleft()
if node == target:
return turns
for combination in new_combinations(node):
if combination not in visited:
visited.add(combination)
q.append((combination, turns + 1))
return -1
@algoses
Перед вами замок с 4 вращающимися дисками. Каждый диск имеет 10 слотов: '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'. Диски можно свободно вращать вперёд и назад: например, вы можете превратить '9' в '0' или '0' в '9'. Каждый ход - это поворот одного диска на одну позицию.
Замок начинает с комбинации '0000' (строка, представляющая состояние 4-х дисков).
Вам дан список тупиков - deadends: если на замке отображается какая-либо из запрещённых комбинаций, диски замка перестают вращаться, и вы не можете его открыть.
Вам также дана целевая комбинация target, представляющая значение дисков, при котором замок откроется. Верните минимальное возможное количество вращений, необходимое для открытия замка. Если это невозможно, верните -1.
Пример 1:
Input: deadends = ["0201","0101","0102","1212","2002"], target = "0202"
Output: 6
Объяснение: последовательность допустимых ходов может быть такой: "0000" -> "1000" -> "1100" -> "1200" -> "1201" -> "1202" -> "0202". Обратите внимание, что последовательность типа "0000" -> "0001" -> "0002" -> "0102" -> "0202" будет недопустимой, так как диски замка остановятся после запрещённой комбинации "0102".
Пример 2:
Input: deadends = ["8888"], target = "0009"
Output: 1
Объяснение: мы можем повернуть последний диск в обратном направлении, чтобы перейти от "0000" к "0009".
Пример 3:
Input: deadends = ["8887","8889","8878","8898","8788","8988","7888","9888"], target = "8888"
Output: -1
Объяснение: мы не можем достичь цели, не попав в тупик.
Ограничения:
1 <= deadends.length <= 500
deadends[i].length == 4
target.length == 4
target не может присутствовать в списке deadends
target и deadends[i] состоят только из цифр
НАШ ЧАТ АЛГОРИТМИСТОВ
Решение
Вершины графа - комбинации цифр на 4 дисках, каждый диск - одна позиция в комбинации. Некоторые вершины (deadends) заблокированы, мы не можем посещать их на пути к целевой вершине (target). Граф - невзвешенный, так как все рёбра имеют одинаковый вес (один поворот диска) => используем алгоритм поиска в ширину (BFS).
Начинаем обход с начального состояния '0000' (если оно в списке deadends - до target мы не дойдём, возвращаем -1), добавляем в очередь в виде кортежа (комбинация цифр и количество вращений). Все запрещённые комбинации сразу добавляем во множество visited, чтобы избежать попадания в тупик.
Пока очередь не пуста: извлекаем текущую комбинацию и количество вращений. Если комбинация соответствует target - возвращаем число вращений. Иначе: генерируем 8 следующих комбинаций, увеличивая и уменьшая каждую из 4 цифр (2 направления: 1 поворот диска вперёд и назад). Чтобы реализовать закольцованность дисков: деление по модулю (%10), "9" -> "0" при вращении диска вперёд ((9 + 1) % 10 = 0) и "0" -> "9" при вращении назад ((0 - 1) % 10 = 9).
Проверяем комбинацию: если ещё не посещена - добавляем в visited и помещаем в очередь, увеличивая количество вращений на 1.
Если очередь опустела, а target не достигнута - пути не существует, возвращаем -1.
Сложность
O(V) - по памяти
Код
def openLock(self, deadends: List[str], target: str) -> int:
if "0000" in deadends:
return -1
def new_combinations(node):
result = []
for i in range(4):
next_digit = (int(node[i]) + 1) % 10
result.append(node[:i] + str(next_digit) + node[i + 1:])
prev_digit = (int(node[i]) - 1) % 10
result.append(node[:i] + str(prev_digit) + node[i + 1:])
return result
q = deque([("0000", 0)])
visited = set(deadends)
visited.add("0000")
while q:
node, turns = q.popleft()
if node == target:
return turns
for combination in new_combinations(node):
if combination not in visited:
visited.add(combination)
q.append((combination, turns + 1))
return -1
@algoses
❤10
Задача с собеседования в eBay
Даны n пар скобок. Напишите функцию генерации всех возможных комбинаций правильных скобочных последовательностей.
Пример 1:
Input: n = 3
Output: ["((()))","(()())","(())()","()(())","()()()"]
Пример 2:
Input: n = 1
Output: ["()"]
Ограничения:
1 <= n <= 8
НАШ ЧАТ АЛГОРИТМИСТОВ
Решение
Известно, что правильная скобочная последовательность должна состоять из одинакового количества открывающих и закрывающих скобок, то есть общая длина правильной комбинации - n * 2.
Для решения задачи используем алгоритм поиска с возвратом, где на каждом шаге, в зависимости от условий, добавляем либо открывающую, либо закрывающую скобку. После построения полной правильной комбинации и её сохранения мы не прерываем поиск, а последовательно возвращаемся по стеку вызовов до последней точки выбора и исследуем другие ветки из этой точки.
Создаём два списка:
result - для хранения найденных правильных комбинаций;
combination - временный список для "собирания" текущей комбинации.
Запускаем рекурсивную функцию backtrack(open_count, close_count) для генерации комбинаций, отслеживая кол-во открывающих и закрывающих скобок.
Базовый случай: если длина текущей комбинации достигла n * 2 - добавляем готовую комбинацию в result и возвращаемся.
Рекурсивное ветвление: можем добавить открывающую, если ещё не использованы все n открывающих скобок. Можем добавить закрывающую, если кол-во открывающих скобок больше кол-ва закрывающих, это условие гарантирует, что у нас есть незакрытая открывающая скобка, которую можно закрыть.
Для каждого допустимого выбора:
- добавляем скобку в combination
- вызываем рекурсию с обновленными параметрами
- удаляем последнюю добавленную скобку для возврата к развилке и исследованию других путей построения последовательности.
Функция завершается, когда полностью исследовано дерево возможных комбинаций. Для начального вызова backtrack(0, 0) это означает, что исследованы все пути первого if, а путь второго if невозможен, так как последовательность не может начаться с закрывающей скобки.
Сложность
Экспоненциальная: O(4ⁿ/√n) - по времени (определяется n-ым числом Каталана Cₙ, равным кол-ву правильных скобочных последовательностей для n пар скобок); в кач-ве упрощения иногда говорят о сложности O(2**n)
O(n) - по памяти
Код
class Solution:
def generateParenthesis(self, n: int) -> List[str]:
result = []
combination = []
def backtrack(open_count, close_count):
if len(combination) == 2 * n:
result.append("".join(combination))
return
if open_count < n:
combination.append("(")
backtrack(open_count + 1, close_count)
combination.pop()
if open_count > close_count:
combination.append(")")
backtrack(open_count, close_count + 1)
combination.pop()
backtrack(0, 0)
return result
@algoses
Даны n пар скобок. Напишите функцию генерации всех возможных комбинаций правильных скобочных последовательностей.
Пример 1:
Input: n = 3
Output: ["((()))","(()())","(())()","()(())","()()()"]
Пример 2:
Input: n = 1
Output: ["()"]
Ограничения:
1 <= n <= 8
НАШ ЧАТ АЛГОРИТМИСТОВ
Решение
Для решения задачи используем алгоритм поиска с возвратом, где на каждом шаге, в зависимости от условий, добавляем либо открывающую, либо закрывающую скобку. После построения полной правильной комбинации и её сохранения мы не прерываем поиск, а последовательно возвращаемся по стеку вызовов до последней точки выбора и исследуем другие ветки из этой точки.
Создаём два списка:
result - для хранения найденных правильных комбинаций;
combination - временный список для "собирания" текущей комбинации.
Запускаем рекурсивную функцию backtrack(open_count, close_count) для генерации комбинаций, отслеживая кол-во открывающих и закрывающих скобок.
Базовый случай: если длина текущей комбинации достигла n * 2 - добавляем готовую комбинацию в result и возвращаемся.
Рекурсивное ветвление: можем добавить открывающую, если ещё не использованы все n открывающих скобок. Можем добавить закрывающую, если кол-во открывающих скобок больше кол-ва закрывающих, это условие гарантирует, что у нас есть незакрытая открывающая скобка, которую можно закрыть.
Для каждого допустимого выбора:
- добавляем скобку в combination
- вызываем рекурсию с обновленными параметрами
- удаляем последнюю добавленную скобку для возврата к развилке и исследованию других путей построения последовательности.
Функция завершается, когда полностью исследовано дерево возможных комбинаций. Для начального вызова backtrack(0, 0) это означает, что исследованы все пути первого if, а путь второго if невозможен, так как последовательность не может начаться с закрывающей скобки.
Сложность
O(n) - по памяти
Код
def generateParenthesis(self, n: int) -> List[str]:
result = []
combination = []
def backtrack(open_count, close_count):
if len(combination) == 2 * n:
result.append("".join(combination))
return
if open_count < n:
combination.append("(")
backtrack(open_count + 1, close_count)
combination.pop()
if open_count > close_count:
combination.append(")")
backtrack(open_count, close_count + 1)
combination.pop()
backtrack(0, 0)
return result
@algoses
❤5🔥1
Задача с собеседования в eBay
Дана закодированная строка. Верните её в раскодированном виде.
Правило кодирования: k[закодированная_строка], где закодированная_строка внутри квадратных скобок повторяется ровно k раз. Гарантируется, что k - целое положительное число.
Считайте, что входная строка всегда корректна: нет лишних пробелов, квадратные скобки сформированы правильно и т.д.. Кроме того, можно считать, что исходные данные не содержат цифр, а цифры используются только для указания числа повторов k. Например, не будет таких входных данных, как 3a или 2[4].
Тестовые примеры сгенерированы так, что длина выходной строки никогда не превысит 10⁵.
Пример 1:
Input: s = "3[a]2[bc]"
Output: "aaabcbc"
Пример 2:
Input: s = "3[a2[c]]"
Output: "accaccacc"
Пример 3:
Input: s = "2[abc]3[cd]ef"
Output: "abcabccdcdcdef"
Ограничения:
1 <= s.length <= 30;
s состоит из строчных английских букв, цифр и квадратных скобок "[]";
s гарантированно является корректным входом;
Все целые числа в s находятся в диапазоне [1, 300].
НАШ ЧАТ АЛГОРИТМИСТОВ
Решение
Очевидная сложность раскодирования в том, что скобки могут быть вложенными. Сначала должны обрабатываться внутренние скобки, поэтому будем использовать стек для хранения символов и промежуточных результатов.
Итерируемся по строке s:
Добавляем символ в стек, если он не является закрывающей скобкой.
Если же встречаем закрывающую скобку, начинаем собирать подстроку:
Пока верхний эл-т стека не является открывающей скобкой: извлекаем символы из стека и добавляем в начало substring.
Далее открывающую скобку из стека удаляем: мы обработали содержимое внутри скобок, теперь нужно получить число его повторов, находящееся перед открывающей скобкой.
Так как число k может быть многозначным, необходимо извлекать символы до тех пор, пока стек не пуст и верхний эл-т является цифрой.
Преобразовываем k в целое число и умножаем подстроку на него, добавляем результат в стек.
После того, как строка s будет полностью обработана, возвращаем итоговую строку, содержащую объединённые эл-ты стека.
Сложность
O(maxK^countK * n) - по времени (где maxK - максимальное значение k, countK - количество вложенных k значений (уровень вложенности), а n - максимальная длина закодированной строки)
O(n) - по памяти
Код
class Solution:
def decodeString(self, s: str) -> str:
stack = []
for char in s:
if char != "]":
stack.append(char)
else:
substring = ""
while stack[-1] != "[":
substring = stack.pop() + substring
stack.pop()
k = ""
while stack and stack[-1].isdigit():
k = stack.pop() + k
stack.append(int(k) * substring)
return "".join(stack)
@algoses
Дана закодированная строка. Верните её в раскодированном виде.
Правило кодирования: k[закодированная_строка], где закодированная_строка внутри квадратных скобок повторяется ровно k раз. Гарантируется, что k - целое положительное число.
Считайте, что входная строка всегда корректна: нет лишних пробелов, квадратные скобки сформированы правильно и т.д.. Кроме того, можно считать, что исходные данные не содержат цифр, а цифры используются только для указания числа повторов k. Например, не будет таких входных данных, как 3a или 2[4].
Тестовые примеры сгенерированы так, что длина выходной строки никогда не превысит 10⁵.
Пример 1:
Input: s = "3[a]2[bc]"
Output: "aaabcbc"
Пример 2:
Input: s = "3[a2[c]]"
Output: "accaccacc"
Пример 3:
Input: s = "2[abc]3[cd]ef"
Output: "abcabccdcdcdef"
Ограничения:
1 <= s.length <= 30;
s состоит из строчных английских букв, цифр и квадратных скобок "[]";
s гарантированно является корректным входом;
Все целые числа в s находятся в диапазоне [1, 300].
НАШ ЧАТ АЛГОРИТМИСТОВ
Решение
Итерируемся по строке s:
Добавляем символ в стек, если он не является закрывающей скобкой.
Если же встречаем закрывающую скобку, начинаем собирать подстроку:
Пока верхний эл-т стека не является открывающей скобкой: извлекаем символы из стека и добавляем в начало substring.
Далее открывающую скобку из стека удаляем: мы обработали содержимое внутри скобок, теперь нужно получить число его повторов, находящееся перед открывающей скобкой.
Так как число k может быть многозначным, необходимо извлекать символы до тех пор, пока стек не пуст и верхний эл-т является цифрой.
Преобразовываем k в целое число и умножаем подстроку на него, добавляем результат в стек.
После того, как строка s будет полностью обработана, возвращаем итоговую строку, содержащую объединённые эл-ты стека.
Сложность
O(n) - по памяти
Код
def decodeString(self, s: str) -> str:
stack = []
for char in s:
if char != "]":
stack.append(char)
else:
substring = ""
while stack[-1] != "[":
substring = stack.pop() + substring
stack.pop()
k = ""
while stack and stack[-1].isdigit():
k = stack.pop() + k
stack.append(int(k) * substring)
return "".join(stack)
@algoses
🔥7❤2😭2