fa
Feedback
Kotlin | LeetCode

Kotlin | LeetCode

رفتن به کانال در Telegram

Сайт: https://easyoffer.ru/ Все каналы: t.me/+xGeAw6ckJ4liYzQy Контакт для рекламы: @easyoffer_adv

نمایش بیشتر
1 738
مشترکین
اطلاعاتی وجود ندارد24 ساعت
-97 روز
-1030 روز

در حال بارگیری داده...

جذب مشترکین
ژوئن '26
ژوئن '26
+8
در 0 کانال‌ها
مه '26
+8
در 0 کانال‌ها
Get PRO
آوریل '26
+9
در 0 کانال‌ها
Get PRO
مارس '26
+18
در 0 کانال‌ها
Get PRO
فوریه '26
+9
در 0 کانال‌ها
Get PRO
ژانویه '26
+14
در 0 کانال‌ها
Get PRO
دسامبر '25
+11
در 0 کانال‌ها
Get PRO
نوامبر '25
+42
در 0 کانال‌ها
Get PRO
اکتبر '25
+41
در 0 کانال‌ها
Get PRO
سپتامبر '25
+28
در 0 کانال‌ها
Get PRO
اوت '25
+43
در 0 کانال‌ها
Get PRO
ژوئیه '25
+44
در 1 کانال‌ها
Get PRO
ژوئن '25
+52
در 0 کانال‌ها
Get PRO
مه '25
+62
در 0 کانال‌ها
Get PRO
آوریل '25
+57
در 0 کانال‌ها
Get PRO
مارس '25
+93
در 1 کانال‌ها
Get PRO
فوریه '25
+100
در 0 کانال‌ها
Get PRO
ژانویه '25
+145
در 53 کانال‌ها
Get PRO
دسامبر '24
+49
در 0 کانال‌ها
Get PRO
نوامبر '24
+70
در 0 کانال‌ها
Get PRO
اکتبر '24
+193
در 12 کانال‌ها
Get PRO
سپتامبر '24
+640
در 333 کانال‌ها
Get PRO
اوت '24
+90
در 0 کانال‌ها
Get PRO
ژوئیه '24
+424
در 219 کانال‌ها
Get PRO
ژوئن '24
+418
در 232 کانال‌ها
تاریخ
رشد مشترکین
اشارات
کانال‌ها
25 ژوئن0
24 ژوئن0
23 ژوئن0
22 ژوئن0
21 ژوئن0
20 ژوئن0
19 ژوئن+1
18 ژوئن0
17 ژوئن+1
16 ژوئن+1
15 ژوئن0
14 ژوئن0
13 ژوئن0
12 ژوئن0
11 ژوئن0
10 ژوئن0
09 ژوئن0
08 ژوئن+2
07 ژوئن0
06 ژوئن0
05 ژوئن0
04 ژوئن+1
03 ژوئن+2
02 ژوئن0
01 ژوئن0
پست‌های کانال
Задача: 57. Insert Interval Сложность: medium Вам дан массив непересекающихся интервалов intervals, где intervals[i] = [starti, endi] представляет начало и конец i-го интервала, и массив intervals отсортирован в порядке возрастания по starti. Вам также дан интервал newInterval = [start, end], представляющий начало и конец другого интервала. Вставьте newInterval в массив intervals так, чтобы intervals оставался отсортированным в порядке возрастания по starti и в intervals не было бы перекрывающихся интервалов (при необходимости объедините перекрывающиеся интервалы). Верните массив intervals после вставки. Обратите внимание, что не обязательно модифицировать массив intervals на месте. Вы можете создать новый массив и вернуть его. Пример:
Input: intervals = [[1,3],[6,9]], newInterval = [2,5]
Output: [[1,5],[6,9]]
👨‍💻 Алгоритм: 1⃣ Инициализация переменных: Инициализируются переменные n и i для хранения размера массива интервалов и текущего индекса соответственно, а также пустой массив res для хранения результата. 2⃣Обработка случаев без перекрытия и с перекрытием: В случае отсутствия перекрытия до вставки, проходим через массив интервалов до тех пор, пока конечная точка текущего интервала меньше начальной точки нового интервала. Добавляем текущий интервал в массив res и переходим к следующему. В случае перекрытия, продолжаем обход, пока начальная точка нового интервала меньше или равна конечной точке текущего интервала. Обновляем начальные и конечные точки нового интервала, объединяя перекрывающиеся интервалы в один. 3⃣Обработка интервалов после вставки: Проходим через оставшиеся интервалы после индекса i и добавляем их в массив res. Это включает интервалы, которые следуют после нового интервала и не перекрываются с ним. Возвращаем массив res, содержащий все интервалы с корректно вставленным новым интервалом. 😎 Решение:
class Solution {
    fun insert(intervals: List<List<Int>>, newInterval: MutableList<Int>): List<List<Int>> {
        val n = intervals.size
        var i = 0
        val res = mutableListOf<List<Int>>()

        while (i < n && intervals[i][1] < newInterval[0]) {
            res.add(intervals[i])
            i++
        }

        while (i < n && newInterval[1] >= intervals[i][0]) {
            newInterval[0] = minOf(newInterval[0], intervals[i][0])
            newInterval[1] = maxOf(newInterval[1], intervals[i][1])
            i++
        }
        res.add(newInterval)

        while (i < n) {
            res.add(intervals[i])
            i++
        }

        return res
    }
}
Ставь 👍 и забирай 📚 Базу знаний

2
Задача: 751. IP to CIDR Сложность: medium Дан указатель на начало односвязного списка и два целых числа left и right, где left <= right. Необходимо перевернуть узлы списка, начиная с позиции left и заканчивая позицией right, и вернуть измененный список. Пример: Input: ip = "255.0.0.7", n = 10 Output: ["255.0.0.7/32","255.0.0.8/29","255.0.0.16/32"] 👨‍💻 Алгоритм: 1⃣Преобразовать начальный IP-адрес в целое число. 2⃣Пока количество оставшихся IP-адресов n больше нуля: Определить наибольший блок, который начинается с текущего IP-адреса и не превышает количество оставшихся IP-адресов. Добавить этот блок к результату. Увеличить текущий IP-адрес на размер блока. Уменьшить количество оставшихся IP-адресов n. 3⃣Преобразовать блоки обратно в формат CIDR и вернуть их. 😎 Решение: fun ipToInt(ip: String): Int { val parts = ip.split(".").map { it.toInt() } return (parts[0] shl 24) + (parts[1] shl 16) + (parts[2] shl 8) + parts[3] } fun intToIp(num: Int): String { return "${(num shr 24) and 255}.${(num shr 16) and 255}.${(num shr 8) and 255}.${num and 255}" } fun cidr(ip: String, prefixLength: Int): String { return "$ip/$prefixLength" } fun findCidrBlocks(startIp: String, n: Int): List<String> { var start = ipToInt(startIp) val result = mutableListOf<String>() var remaining = n while (remaining > 0) { var maxSize = 1 while (maxSize <= start && maxSize <= remaining) { maxSize = maxSize shl 1 } maxSize = maxSize shr 1 while (start % maxSize != 0) { maxSize = maxSize shr 1 } result.add(cidr(intToIp(start), 32 - Integer.bitCount(maxSize - 1) + 1)) start += maxSize remaining -= maxSize } return result } Ставь 👍 и забирай 📚 Базу знаний
81
3
Задача: 935. Knight Dialer Сложность: medium Шахматный конь обладает уникальным движением: он может перемещаться на две клетки по вертикали и одну клетку по горизонтали, или на две клетки по горизонтали и одну клетку по вертикали (при этом обе клетки образуют форму буквы L). Возможные движения шахматного коня показаны на этой диаграмме: Шахматный конь может двигаться так, как показано на шахматной диаграмме ниже: У нас есть шахматный конь и телефонная панель, как показано ниже, конь может стоять только на числовой клетке (то есть на синей клетке). Учитывая целое число n, верните, сколько различных телефонных номеров длины n мы можем набрать. Вам разрешается сначала поставить коня на любую цифровую клетку, а затем выполнить n - 1 прыжков, чтобы набрать номер длины n. Все прыжки должны быть правильными прыжками коня. Поскольку ответ может быть очень большим, верните ответ по модулю 10^9 + 7. Пример: Input: n = 1 Output: 10 👨‍💻 Алгоритм: 1⃣Определить возможные движения коня с каждой цифровой клетки. Использовать динамическое программирование для хранения количества способов достижения каждой цифровой клетки на каждом шаге. 2⃣Инициализировать массив DP количеством способов набора телефонного номера длины 1 для каждой цифровой клетки (это просто 1). На каждом шаге обновлять массив DP, переходя по всем возможным движениям коня. 3⃣Вернуть сумму всех значений в массиве DP на последнем шаге. 😎 Решение: class Solution { fun knightDialer(n: Int): Int { val MOD = 1000000007 val moves = arrayOf( intArrayOf(4, 6), intArrayOf(6, 8), intArrayOf(7, 9), intArrayOf(4, 8), intArrayOf(0, 3, 9), intArrayOf(), intArrayOf(0, 1, 7), intArrayOf(2, 6), intArrayOf(1, 3), intArrayOf(2, 4) ) var dp = IntArray(10) { 1 } for (step in 1 until n) { val newDp = IntArray(10) for (i in 0 until 10) { for (move in moves[i]) { newDp[move] = (newDp[move] + dp[i]) % MOD } } dp = newDp } return dp.fold(0) { acc, count -> (acc + count) % MOD } } } Ставь 👍 и забирай 📚 Базу знаний
90
4
Осталось 3 часа до конца акции: «Пожизненный PRO тариф — по цене 1 года» Поиск работы отнимает силы, время и веру в себя, но
Осталось 3 часа до конца акции: «Пожизненный PRO тариф — по цене 1 года» Поиск работы отнимает силы, время и веру в себя, но не у тех кто использует easyoffer PRO. Успей сделать самую выгодную инвестицию в развитие своей карьеры. Акция закончится уже сегодня 23 июня 23:59 по мск: 👉 https://easyoffer.ru/pro
46
5
Последний день акции: «Пожизненный PRO тариф — по цене 1 года» 🚀 PRO включает: – Полный доступ ко всем грейдам и профессиям
Последний день акции: «Пожизненный PRO тариф — по цене 1 года» 🚀 PRO включает: – Полный доступ ко всем грейдам и профессиям – База live-coding задач и вопросов из технических собеседований с вероятностью их встречи – Примеры лучших ответов от Senior разработчиков – 1100+ записи реальных собеседований, в том числе в топовые компании (Сбер, Авито, Яндекс, WB, OZON, МТС и др.) – База 400+ тестовых заданий от компаний. – Автоотклики на вакансии в хедхантер – Аналитика ТОП-требований из вакансий для лучшего написания резюме и прохождения ATS систем рекрутеров – Генератор уникального резюме и CV под каждую вакансию – Тренажеры подготовки к собеседованию: «Реальное собеседование» и «Проработка вопросов» по методике интервальных повторений (как Anki) – (скоро) Агрегатор вакансий – (скоро) Сообщество Акция закончится уже сегодня 23 июня 23:59 по мск: 👉 https://easyoffer.ru/pro
88
6
Задача: 841. Keys and Rooms Сложность: medium Есть n комнат, пронумерованных от 0 до n - 1, и все комнаты закрыты, кроме комнаты 0. Ваша цель — посетить все комнаты. Однако вы не можете войти в закрытую комнату, не имея ключа от нее. Когда вы посещаете комнату, вы можете найти в ней набор различных ключей. Каждый ключ имеет номер, указывающий, какую комнату он открывает, и вы можете взять их все с собой, чтобы открыть другие комнаты. Дан массив rooms, где rooms[i] — это набор ключей, которые вы можете получить, если посетите комнату i. Верните true, если вы можете посетить все комнаты, или false в противном случае. Пример: Input: rooms = [[1],[2],[3],[]] Output: true Explanation: We visit room 0 and pick up key 1. We then visit room 1 and pick up key 2. We then visit room 2 and pick up key 3. We then visit room 3. Since we were able to visit every room, we return true. 👨‍💻 Алгоритм: 1⃣Создайте массив seen для отслеживания посещенных комнат и стек stack для ключей, которые нужно использовать. 2⃣Поместите ключ от комнаты 0 в стек и отметьте комнату 0 как посещенную. 3⃣Пока стек не пуст, извлекайте ключи из стека и используйте их для открытия новых комнат, добавляя найденные ключи в стек. Если все комнаты посещены, верните true, иначе false. 😎 Решение: class Solution { fun canVisitAllRooms(rooms: List<List<Int>>): Boolean { val seen = BooleanArray(rooms.size) { false } seen[0] = true val stack = Stack<Int>() stack.push(0) while (stack.isNotEmpty()) { val node = stack.pop() for (nei in rooms[node]) { if (!seen[nei]) { seen[nei] = true stack.push(nei) } } } return seen.all { it } } } Ставь 👍 и забирай 📚 Базу знаний
124
7
Пожизненный PRO тариф — по цене 1 года. Покупаешь один раз — пользуешься всю жизнь: 👉 https://easyoffer.ru/pro 🚀 PRO-доступ
Пожизненный PRO тариф — по цене 1 года. Покупаешь один раз — пользуешься всю жизнь: 👉 https://easyoffer.ru/pro 🚀 PRO-доступ закроет 99% проблем на пути к офферу: 1. Полный доступ ко всем грейдам и профессиям. Не важно, Junior вы или Senior, Тестировщик, Разработчик, Проджект — вы получите материалы под ваш текущий уровень и цели, без ограничений. 2. База live-coding задач и вопросов с реальных собесов с уникальной системой вероятности их встречи. Вы будете готовиться не вслепую, а точечно по тем темам, которые спрашивают чаще всего. 3. Эталонные ответы от Senior-разработчиков. Никакой воды и догадок — только четкие, структурированные решения, за которые дают «зеленый свет» к офферу 4. 1100+ записей реальных собеседований (включая топы: Сбер, Авито, Яндекс, WB, OZON, МТС). Вы увидите всё изнутри: как спрашивают, как отвечают сильные кандидаты и на каких ошибках проваливаются 80% проходящих. 5. База 400+ тестовых заданий. Если вы еще студент, то практикуйтесь на решении задач, которые помогут попасть на собес 6. Автоотклики на Хедхантере — пока вы спите, ваше резюме летит к рекрутерам автоматически. Это экономия сотен часов ручного кликанья. 7. Аналитика ТОП-требований из вакансий. Мы парсим рынок и показываем, какие скиллы сейчас в цене. Это позволит вам точечно апгрейдить резюме и проходить суровые ATS-фильтры (которые отсеивают до 75% резюме еще до просмотра рекрутером). 8. Генератор уникального резюме и CV под каждую вакансию. Забудьте про «универсальное» резюме — нейросеть адаптирует ваш опыт под конкретную позицию за минуту, повышая шансы на приглашение в разы. 9. Тренажеры подготовки к собеседованию: «Реальное собеседование» — сценарий вопросов из реальных интервью «Проработка вопросов» — флеш карточки с вопросами/ответами по методике интервальных повторений (как Anki) 10. (Скоро) Агрегатор вакансий — все вакансии из HH, Telegram, LinkedIn и других площадок в одной ленте. 11. (Скоро) Закрытое комьюнити — нетворкинг и помощь в сложных вопросах от таких же целеустремленных айтишников. Завтра последний день акции: 👉 https://easyoffer.ru/pro
92
8
Задача: 1255. Maximum Score Words Formed by Letters Сложность: hard Даны список слов, список отдельных букв (могут повторяться) и оценка каждого символа. Верните максимальную оценку любого правильного набора слов, образованного с помощью заданных букв (words[i] не может быть использовано два или более раз). Не обязательно использовать все символы в буквах, каждая буква может быть использована только один раз. Оценка букв 'a', 'b', 'c', ... , 'z' задаются значениями score[0], score[1], ... , score[25] соответственно. Пример: Input: words = ["dog","cat","dad","good"], letters = ["a","a","c","d","d","d","g","o","o"], score = [1,0,9,5,0,0,3,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0] Output: 23 👨‍💻 Алгоритм: 1⃣Создайте функцию для вычисления оценки слова. 2⃣Используйте метод перебора подмножеств (или битовое представление всех подмножеств) для нахождения всех возможных комбинаций слов. Для каждой комбинации проверяйте, можно ли составить каждое слово из доступных букв. 3⃣Вычислите суммарную оценку для каждой допустимой комбинации слов и сохраните максимальную оценку. 😎 Решение: class Solution { fun maxScoreWords(words: Array<String>, letters: CharArray, score: IntArray): Int { val letterCount = mutableMapOf<Char, Int>() letters.forEach { letterCount[it] = letterCount.getOrDefault(it, 0) + 1 } fun wordScore(word: String): Int { var total = 0 for (ch in word) { total += score[ch - 'a'] } return total } fun canFormWord(word: String, letterCount: Map<Char, Int>): Boolean { val count = mutableMapOf<Char, Int>() for (ch in word) { count[ch] = count.getOrDefault(ch, 0) + 1 if (count[ch]!! > letterCount.getOrDefault(ch, 0)) { return false } } return true } var maxScore = 0 val n = words.size for (i in 1 until (1 shl n)) { var currScore = 0 val usedLetters = mutableMapOf<Char, Int>() var valid = true for (j in 0 until n) { if (i and (1 shl j) != 0) { val word = words[j] if (canFormWord(word, letterCount)) { currScore += wordScore(word) for (ch in word) { usedLetters[ch] = usedLetters.getOrDefault(ch, 0) + 1 } } else { valid = false break } } } if (valid) { maxScore = maxOf(maxScore, currScore) } } return maxScore } } Ставь 👍 и забирай 📚 Базу знаний
83
9
Задача: 136. Single Number Сложность: easy Дан непустой массив целых чисел nums, в котором каждый элемент встречается дважды, кроме одного. Найдите этот единственный элемент. Вы должны реализовать решение с линейной сложностью выполнения и использовать только постоянное дополнительное пространство. Пример: Input: nums = [2,2,1] Output: 1 👨‍💻 Алгоритм: 1⃣Переберите все элементы в массиве nums. 2⃣Если какое-то число в nums новое для массива, добавьте его. 3⃣Если какое-то число уже есть в массиве, удалите его. 😎 Решение: fun singleNumber(nums: IntArray): Int { val noDuplicateList = mutableListOf<Int>() for (i in nums) { if (i !in noDuplicateList) { noDuplicateList.add(i) } else { noDuplicateList.removeAll { it == i } } } return noDuplicateList.firstOrNull() ?: 0 } Ставь 👍 и забирай 📚 Базу знаний
105
10
Задача: 838. Push Dominoes Сложность: medium Есть n домино, выстроенные в линию, и каждое домино стоит вертикально. Вначале мы одновременно толкаем некоторые домино либо влево, либо вправо. Через каждую секунду каждое падающее влево домино толкает соседнее домино слева. Точно так же домино, падающие вправо, толкают соседние домино, стоящие справа. Когда вертикальное домино оказывается под воздействием падающих домино с обеих сторон, оно остаётся неподвижным из-за баланса сил. В рамках этой задачи мы будем считать, что падающее домино не передаёт дополнительную силу падающему или уже упавшему домино. Вам дано строковое представление начального состояния домино: dominoes[i] = 'L', если i-е домино толкнули влево, dominoes[i] = 'R', если i-е домино толкнули вправо, и dominoes[i] = '.', если i-е домино не было толкнуто. Верните строку, представляющую конечное состояние. Пример: Input: dominoes = ".L.R...LR..L.." Output: "LL.RR.LLRRLL.." 👨‍💻 Алгоритм: 1⃣Пройдите по строке и сохраните индексы и символы не пустых домино в массивы. 2⃣Добавьте фиктивные домино 'L' в начале и 'R' в конце для упрощения логики. 3⃣Обработайте промежутки между соседними домино, обновляя их состояния согласно правилам. 😎 Решение: class Solution { fun pushDominoes(dominoes: String): String { val N = dominoes.length val indexes = mutableListOf(-1) val symbols = mutableListOf('L') for (i in dominoes.indices) { if (dominoes[i] != '.') { indexes.add(i) symbols.add(dominoes[i]) } } indexes.add(N) symbols.add('R') val ans = dominoes.toCharArray() for (idx in 0 until indexes.size - 1) { val i = indexes[idx] val j = indexes[idx + 1] val x = symbols[idx] val y = symbols[idx + 1] if (x == y) { for (k in i + 1 until j) { ans[k] = x } } else if (x == 'R' && y == 'L') { for (k in i + 1 until j) { if (k - i == j - k) { ans[k] = '.' } else if (k - i < j - k) { ans[k] = 'R' } else { ans[k] = 'L' } } } } return String(ans) } Ставь 👍 и забирай 📚 Базу знаний
102
11
Привет, ребята! У нас для вас отличные новости — на easyoffer вышло сразу несколько крупных обновлений: 1. Автоотклики на HeadHunter Снова работают в полную силу — можно смело возвращаться к активному поиску. 2. Новый раздел «Резюмейкер» Теперь вы можете быстро создавать уникальные резюме, адаптированные под каждую вакансию, и сразу добавлять сопроводительное письмо. Это заметно повышает шансы получить приглашение на собеседование. 3. База вопросов стала чище Мы навели порядок и удалили около 30% дубликатов. Ориентироваться стало проще. –––––––––––––––––– 🔥 Акция в честь обновления Пожизненный тариф easyoffer PRO — по цене одного года. Успейте до 23 июня: 👉 https://easyoffer.ru/pro –––––––––––––––––– Что дальше? В ближайшие пару недель добавим ещё два раздела: 1. Сообщество с чатами по всем профессиональным направлениям. 2. Агрегатор вакансий, чтобы поиск работы стал ещё удобнее.
89
12
Задача: 219. Contains Duplicate II Сложность: easy Дан массив целых чисел nums и целое число k. Верните true, если в массиве существуют два различных индекса i и j, такие что nums[i] == nums[j] и abs(i - j) <= k. Пример: Input: nums = [1,2,3,1,2,3], k = 2 Output: false 👨‍💻 Алгоритм: 1⃣Создайте пустое множество set. 2⃣Пройдитесь по массиву nums: Если текущий элемент уже есть в множестве, верните true. Добавьте текущий элемент в множество. Если размер множества больше k, удалите элемент, который был добавлен k шагов назад. 3⃣Если не найдены дублирующиеся элементы на расстоянии k или менее, верните false. 😎 Решение: class Solution { fun containsNearbyDuplicate(nums: IntArray, k: Int): Boolean { val set = mutableSetOf<Int>() for (i in nums.indices) { if (set.contains(nums[i])) return true set.add(nums[i]) if (set.size > k) set.remove(nums[i - k]) } return false } } Ставь 👍 и забирай 📚 Базу знаний
110
13
Задача: 1063. Number of Valid Subarrays Сложность: hard Дан целочисленный массив nums. Вернуть количество непустых подмассивов, в которых левый элемент не больше других элементов подмассива. Подмассив — это непрерывная часть массива. Пример: Input: nums = [1,4,2,5,3] Output: 11 Explanation: There are 11 valid subarrays: [1],[4],[2],[5],[3],[1,4],[2,5],[1,4,2],[2,5,3],[1,4,2,5],[1,4,2,5,3]. 👨‍💻 Алгоритм: 1⃣нициализируйте переменную ans значением 0. Инициализируйте пустой стек st, который будет хранить индексы элементов в стеке. 2⃣Итерируйте по элементам массива nums для каждого индекса i: продолжайте извлекать элементы из стека st, пока стек не станет пустым или элемент nums[i] не станет больше элемента на вершине стека. Для каждого извлеченного элемента добавляйте количество подмассивов как i - st.top(). Поместите текущий индекс i в стек. 3⃣Извлеките все оставшиеся элементы из стека и для каждого рассмотрите размер nums как индекс следующего меньшего элемента. Соответственно, добавьте nums.size() - st.top() к переменной ans. Верните ans. 😎 Решение: class Solution { fun validSubarrays(nums: IntArray): Int { var ans = 0 val st = mutableListOf<Int>() for (i in nums.indices) { while (st.isNotEmpty() && nums[i] < nums[st.last()]) { ans += (i - st.removeAt(st.size - 1)) } st.add(i) } while (st.isNotEmpty()) { ans += (nums.size - st.removeAt(st.size - 1)) } return ans } } Ставь 👍 и забирай 📚 Базу знаний
117
14
Задача: 726. Number of Atoms Сложность: hard Если задана строковая формула, представляющая химическую формулу, верните количество атомов. Атомный элемент всегда начинается с прописного символа, затем ноль или более строчных букв, представляющих его название. Если количество больше 1, за ним может следовать одна или более цифр, представляющих количество элементов. Например, "H2O" и "H2O2" возможны, а "H1O2" невозможен. Две формулы объединяются вместе, чтобы получить другую формулу. Например, "H2O2He3Mg4" также является формулой. Формула, заключенная в круглые скобки, и счет (по желанию) также являются формулами. Например, "(H2O2)" и "(H2O2)3" являются формулами. Возвращает количество всех элементов в виде строки в следующем виде: первое имя (в отсортированном порядке), затем его количество (если это количество больше 1), затем второе имя (в отсортированном порядке), затем его количество (если это количество больше 1) и т. д. Тестовые примеры генерируются таким образом, чтобы все значения в выводе помещались в 32-битное целое число. Пример: Input: formula = "H2O" Output: "H2O" 👨‍💻 Алгоритм: 1⃣Используйте стек для отслеживания текущего уровня скобок. 2⃣Пройдите по строке формулы, анализируя каждый символ: Если символ - это открывающая скобка '(', создайте новый словарь для хранения атомов внутри скобок. Если символ - это закрывающая скобка ')', извлеките словарь из стека и умножьте количества атомов на последующее число, если оно присутствует. Если символ - это атом (начинается с заглавной буквы), извлеките имя атома и его количество, и добавьте его в текущий словарь. 3⃣После завершения обработки строки, объедините все словари из стека и отсортируйте результат. 😎 Решение: class Solution { fun countOfAtoms(formula: String): String { val stack = ArrayDeque<MutableMap<String, Int>>() stack.push(mutableMapOf()) var i = 0 val n = formula.length while (i < n) { if (formula[i] == '(') { stack.push(mutableMapOf()) i++ } else if (formula[i] == ')') { val top = stack.pop() i++ var multiplicity = 0 while (i < n && formula[i].isDigit()) { multiplicity = multiplicity * 10 + (formula[i] - '0') i++ } multiplicity = if (multiplicity == 0) 1 else multiplicity for ((name, count) in top) { stack.peek()[name] = stack.peek().getOrDefault(name, 0) + count * multiplicity } } else { var start = i i++ while (i < n && formula[i].isLowerCase()) { i++ } val name = formula.substring(start, i) start = i var multiplicity = 0 while (i < n && formula[i].isDigit()) { multiplicity = multiplicity * 10 + (formula[i] - '0') i++ } multiplicity = if (multiplicity == 0) 1 else multiplicity stack.peek()[name] = stack.peek().getOrDefault(name, 0) + multiplicity } } val countMap = stack.pop() val result = StringBuilder() for (name in countMap.keys.sorted()) { result.append(name) val count = countMap[name]!! if (count > 1) { result.append(count) } } return result.toString() } } Ставь 👍 и забирай 📚 Базу знаний
116
15
Задача: 1143. Longest Common Subsequence Сложность: medium Даны две строки text1 и text2. Верните длину их наибольшей общей подпоследовательности. Если общей подпоследовательности нет, верните 0. Подпоследовательность строки — это новая строка, созданная из оригинальной строки путем удаления некоторых символов (может быть ни одного) без изменения относительного порядка оставшихся символов. Например, "ace" является подпоследовательностью "abcde". Общая подпоследовательность двух строк — это подпоследовательность, которая является общей для обеих строк. Пример: Input: text1 = "abcde", text2 = "ace" Output: 3 Explanation: The longest common subsequence is "ace" and its length is 3. 👨‍💻 Алгоритм: 1⃣Создайте двумерный массив memo для хранения промежуточных результатов, чтобы избежать повторных вычислений. Инициализируйте массив значением -1, чтобы указать, что эти ячейки еще не были рассчитаны. 2⃣Реализуйте рекурсивную функцию memoSolve, которая принимает два указателя на текущие позиции в text1 и text2 и возвращает длину наибольшей общей подпоследовательности для этих подстрок. Если текущие символы совпадают, добавьте 1 к результату рекурсивного вызова для следующих символов. Если не совпадают, найдите максимум между рекурсивными вызовами с измененными указателями. 3⃣Возвращайте значение memoSolve(0, 0), чтобы получить результат для всей строки. 😎 Решение: class Solution { private lateinit var memo: Array<IntArray> private lateinit var text1: String private lateinit var text2: String fun longestCommonSubsequence(text1: String, text2: String): Int { this.text1 = text1 this.text2 = text2 memo = Array(text1.length + 1) { IntArray(text2.length + 1) { -1 } } return memoSolve(0, 0) } private fun memoSolve(p1: Int, p2: Int): Int { if (p1 == text1.length || p2 == text2.length) return 0 if (memo[p1][p2] != -1) return memo[p1][p2] val answer: Int if (text1[p1] == text2[p2]) { answer = 1 + memoSolve(p1 + 1, p2 + 1) } else { answer = maxOf(memoSolve(p1, p2 + 1), memoSolve(p1 + 1, p2)) } memo[p1][p2] = answer return answer } } Ставь 👍 и забирай 📚 Базу знаний
116
16
Задача: 1029. Two City Scheduling Сложность: medium Компания планирует провести интервью с 2n людьми. Учитывая массив costs, где costs[i] = [aCosti, bCosti], стоимость перелета i-го человека в город a равна aCosti, а стоимость перелета i-го человека в город b равна bCosti. Выведите минимальную стоимость перелета каждого человека в город, чтобы в каждый город прибыло ровно n человек. Пример: Input: traversal = "1-2--3--4-5--6--7" Output: [1,2,5,3,4,6,7] 👨‍💻 Алгоритм: 1⃣Вычислить разницу стоимости: Для каждого человека вычислите разницу в стоимости между перелетом в город A и город B. 2⃣Сортировать по разнице: Отсортируйте людей по разнице в стоимости перелета в город A и B. Это поможет минимизировать общую стоимость, так как мы сначала будем отправлять тех, для кого разница минимальна. 3⃣Назначить города: Первые n человек из отсортированного списка отправьте в город A. Оставшихся n человек отправьте в город B. 😎 Решение: class Solution { fun twoCitySchedCost(costs: Array<IntArray>): Int { costs.sortBy { it[0] - it[1] } var totalCost = 0 val n = costs.size / 2 for (i in 0 until n) { totalCost += costs[i][0] totalCost += costs[i + n][1] } return totalCost } } Ставь 👍 и забирай 📚 Базу знаний
132
17
Задача: 395. Longest Substring with At Least K Repeating Characters Сложность: medium Дана строка s и целое число k, верните длину самой длинной подстроки строки s, такая что частота каждого символа в этой подстроке больше или равна k. Если такой подстроки не существует, верните 0. Пример: Input: s = "aaabb", k = 3 Output: 3 Explanation: The longest substring is "aaa", as 'a' is repeated 3 times. 👨‍💻 Алгоритм: 1⃣Генерируйте подстроки из строки s, начиная с индекса start и заканчивая индексом end. Используйте массив countMap для хранения частоты каждого символа в подстроке. 2⃣Метод isValid использует countMap для проверки, что каждый символ в подстроке встречается как минимум k раз. Если условие выполняется, текущая подстрока считается допустимой. 3⃣Отслеживайте максимальную длину допустимой подстроки, обновляя её, когда найдена более длинная подстрока, удовлетворяющая условиям. В конце возвращайте длину самой длинной подстроки. 😎 Решение: class Solution { fun longestSubstring(s: String, k: Int): Int { if (s.isEmpty() || k > s.length) { return 0 } val n = s.length var result = 0 for (start in 0 until n) { val countMap = IntArray(26) for (end in start until n) { countMap[s[end] - 'a']++ if (isValid(countMap, k)) { result = maxOf(result, end - start + 1) } } } return result } private fun isValid(countMap: IntArray, k: Int): Boolean { var countLetters = 0 var countAtLeastK = 0 for (count in countMap) { if (count > 0) countLetters++ if (count >= k) countAtLeastK++ } return countLetters == countAtLeastK } } Ставь 👍 и забирай 📚 Базу знаний
139
18
Задача: 1335. Minimum Difficulty of a Job Schedule Сложность: hard Вы хотите составить расписание списка заданий на d дней. Задания зависят друг от друга (т.е. чтобы выполнить задание i, вы должны закончить все задания j, где 0 <= j < i). Вы должны выполнять как минимум одно задание каждый день. Сложность расписания заданий — это сумма сложностей каждого дня из d дней. Сложность дня — это максимальная сложность задания, выполненного в этот день. Вам дан массив целых чисел jobDifficulty и целое число d. Сложность i-го задания равна jobDifficulty[i]. Верните минимальную сложность расписания заданий. Если вы не можете найти расписание для заданий, верните -1. Пример: Input: jobDifficulty = [6,5,4,3,2,1], d = 2 Output: 7 Explanation: First day you can finish the first 5 jobs, total difficulty = 6. Second day you can finish the last job, total difficulty = 1. The difficulty of the schedule = 6 + 1 = 7 👨‍💻 Алгоритм: 1⃣Определение состояния: Индекс i (индекс первой задачи на сегодняшний день в массиве jobDifficulty) и день d (количество оставшихся дней) будут определять состояние динамического программирования. min_diff(i, d) обозначает минимальную сложность при начале с i-й задачи с оставшимися d днями. min_diff(0, d) будет окончательным ответом, так как он представляет начало с начала массива задач и завершение всех задач за ровно d дней. 2⃣Функция перехода состояния: Задача с индексом j будет первой задачей на следующий день, следовательно, задачи, которые должны быть завершены сегодня, это все задачи с индексами между i и j, то есть jobDifficulty[i ]. Поскольку сложность дня — это максимальная сложность выполненной в этот день задачи, к сумме сложностей задач будет добавляться максимум из jobDifficulty[i ]. Формулируем функцию перехода состояния следующим образом: min_diff(i, d) = min_diff(j, d - 1) + max(jobDifficulty[i ]) для всех j > i. 3⃣Базовый случай: Когда остается только 1 день, необходимо завершить все незавершенные задачи в этот день и увеличить сложность расписания задач на максимальную сложность этих задач. 😎 Решение: class Solution { fun minDifficulty(jobDifficulty: IntArray, d: Int): Int { val n = jobDifficulty.size if (n < d) return -1 val mem = Array(n) { IntArray(d + 1) { -1 } } fun minDiff(i: Int, daysRemaining: Int): Int { if (mem[i][daysRemaining] != -1) { return mem[i][daysRemaining] } if (daysRemaining == 1) { return jobDifficulty.sliceArray(i until n).maxOrNull()!! } var res = Int.MAX_VALUE var dailyMaxJobDiff = 0 for (j in i until n - daysRemaining + 1) { dailyMaxJobDiff = maxOf(dailyMaxJobDiff, jobDifficulty[j]) res = minOf(res, dailyMaxJobDiff + minDiff(j + 1, daysRemaining - 1)) } mem[i][daysRemaining] = res return res } return minDiff(0, d) } } Ставь 👍 и забирай 📚 Базу знаний
124
19
Задача: 56. Merge Intervals Сложность: medium Дан массив интервалов, где intervals[i] = [starti, endi]. Объедините все перекрывающиеся интервалы и верните массив неперекрывающихся интервалов, которые покрывают все интервалы во входных данных. Пример: Input: intervals = [[1,3],[2,6],[8,10],[15,18]] Output: [[1,6],[8,10],[15,18]] Explanation: Since intervals [1,3] and [2,6] overlap, merge them into [1,6]. 👨‍💻 Алгоритм: 1⃣Представление графа: Имея представленную интуицию, мы можем изобразить граф в виде списка смежности, вставляя направленные ребра в обоих направлениях, чтобы симулировать неориентированные ребра. 2⃣Определение компонент связности: Для определения, в какой компоненте связности находится каждый узел, мы выполняем обходы графа от произвольных непосещенных узлов до тех пор, пока все узлы не будут посещены. Для эффективности мы храним посещенные узлы в множестве (Set), что позволяет проводить проверки на принадлежность и вставку за константное время. 3⃣Объединение интервалов внутри компонент: Наконец, мы рассматриваем каждую связную компоненту, объединяя все её интервалы, создавая новый интервал с началом, равным минимальному началу среди всех интервалов в компоненте, и концом, равным максимальному концу. 😎 Решение: class Solution { private fun overlap(a: IntArray, b: IntArray): Boolean { return a[0] <= b[1] && b[0] <= a[1] } private fun buildGraph(intervals: Array<IntArray>): HashMap<List<Int>, MutableList<IntArray>> { val graph = HashMap<List<Int>, MutableList<IntArray>>() for (i in intervals.indices) { for (j in i + 1 until intervals.size) { if (overlap(intervals[i], intervals[j])) { graph.getOrPut(intervals[i].toList()) { mutableListOf() }.add(intervals[j]) graph.getOrPut(intervals[j].toList()) { mutableListOf() }.add(intervals[i]) } } } return graph } private fun mergeNodes(nodes: List<IntArray>): IntArray { val minStart = nodes.minOf { it[0] } val maxEnd = nodes.maxOf { it[1] } return intArrayOf(minStart, maxEnd) } private fun getComponents( graph: HashMap<List<Int>, MutableList<IntArray>>, intervals: Array<IntArray> ): Pair<HashMap<Int, MutableList<IntArray>>, Int> { val visited = mutableSetOf<List<Int>>() var compNumber = 0 val nodesInComp = HashMap<Int, MutableList<IntArray>>() fun markComponentDFS(start: IntArray) { val stack = mutableListOf(start) while (stack.isNotEmpty()) { val node = stack.removeLast().toList() if (node !in visited) { visited.add(node) nodesInComp.getOrPut(compNumber) { mutableListOf() }.add(start) graph[node]?.forEach { stack.add(it) } } } } for (interval in intervals) { if (interval.toList() !in visited) { markComponentDFS(interval) compNumber++ } } return Pair(nodesInComp, compNumber) } fun merge(intervals: Array<IntArray>): Array<IntArray> { val (graph, nodesInComp, numberComps) = buildGraph(intervals).let { val (nodesInComp, numberComps) = getComponents(it, intervals) Triple(it, nodesInComp, numberComps) } return Array(numberComps) { comp -> mergeNodes(nodesInComp[comp]!!) } } } Ставь 👍 и забирай 📚 Базу знаний
127
20
Задача: 1229. Meeting Scheduler Сложность: medium Даны массивы доступных временных слотов slots1 и slots2 для двух человек и продолжительность встречи duration, верните самый ранний временной слот, который подходит обоим и имеет продолжительность duration. Если нет общего временного слота, который удовлетворяет требованиям, верните пустой массив. Формат временного слота — это массив из двух элементов [start, end], представляющий инклюзивный временной диапазон от start до end. Гарантируется, что никакие два доступных временных слота одного и того же человека не пересекаются друг с другом. То есть для любых двух временных слотов [start1, end1] и [start2, end2] одного и того же человека либо start1 > end2, либо start2 > end1. Пример: Input: slots1 = [[10,50],[60,120],[140,210]], slots2 = [[0,15],[60,70]], duration = 8 Output: [60,68] 👨‍💻 Алгоритм: 1⃣Отсортируйте оба массива slots1 и slots2 по времени начала. 2⃣Инициализируйте два указателя, указывающие на начало slots1 и slots2 соответственно. 3⃣Перебирайте, пока указатель1 не достигнет конца slots1 или указатель2 не достигнет конца slots2: Найдите общий слот между slots1[pointer1] и slots2[pointer2]. Если общий слот больше или равен duration, верните результат. В противном случае найдите слот, который заканчивается раньше, и передвиньте указатель. Если общий слот не найден, верните пустой массив. 😎 Решение: class Solution { fun minAvailableDuration(slots1: Array<IntArray>, slots2: Array<IntArray>, duration: Int): List<Int> { slots1.sortBy { it[0] } slots2.sortBy { it[0] } var pointer1 = 0 var pointer2 = 0 while (pointer1 < slots1.size && pointer2 < slots2.size) { val intersectLeft = maxOf(slots1[pointer1][0], slots2[pointer2][0]) val intersectRight = minOf(slots1[pointer1][1], slots2[pointer2][1]) if (intersectRight - intersectLeft >= duration) { return listOf(intersectLeft, intersectLeft + duration) } if (slots1[pointer1][1] < slots2[pointer2][1]) { pointer1++ } else { pointer2++ } } return emptyList() } } Ставь 👍 и забирай 📚 Базу знаний
115