Kotlin | LeetCode
1.84K subscribers
167 photos
1.1K links
Cайт easyoffer.ru
Реклама @easyoffer_adv
ВП @easyoffer_vp

Тесты t.iss.one/+Gzg9SH2MNxM0ZTYy
Вопросы соебсов t.iss.one/+OOb6zFa_-Oo3NjZi
Вакансии t.iss.one/+KuGNaHeKkQg1NzAy
Download Telegram
#medium
Задача: 725. Split Linked List in Parts

Учитывая голову односвязного списка и целое число k, разбейте связный список на k последовательных частей связного списка. Длина каждой части должна быть как можно более одинаковой: никакие две части не должны иметь размер, отличающийся более чем на единицу. Это может привести к тому, что некоторые части будут нулевыми. Части должны располагаться в порядке появления во входном списке, и части, появившиеся раньше, всегда должны иметь размер, больший или равный частям, появившимся позже. Возвращается массив из k частей.

Пример:
Input: head = [1,2,3], k = 5
Output: [[1],[2],[3],[],[]]


👨‍💻 Алгоритм:

1⃣Определите общую длину связного списка.

2⃣Вычислите базовый размер каждой части и количество частей, которые должны быть на одну единицу длиннее.

3⃣Разделите список на части, присваивая каждую часть в массив результатов.

😎 Решение:
class ListNode(var `val`: Int) {
var next: ListNode? = null
}

fun splitListToParts(root: ListNode?, k: Int): Array<ListNode?> {
var length = 0
var node = root
while (node != null) {
length++
node = node.next
}

val partLength = length / k
val extraParts = length % k

val parts = Array<ListNode?>(k) { null }
node = root
for (i in 0 until k) {
val partHead = node
val partSize = partLength + if (i < extraParts) 1 else 0
for (j in 0 until partSize - 1) {
if (node != null) node = node.next
}
if (node != null) {
val nextPart = node.next
node.next = null
node = nextPart
}
parts[i] = partHead
}

return parts
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
#medium
Задача: 341. Flatten Nested List Iterator

Вам дан вложенный список целых чисел nestedList. Каждый элемент либо является целым числом, либо списком, элементы которого также могут быть целыми числами или другими списками. Реализуйте итератор для его развёртки.

Реализуйте класс NestedIterator:

NestedIterator(List<NestedInteger> nestedList) Инициализирует итератор вложенным списком nestedList.
int next() Возвращает следующий целый элемент вложенного списка.
boolean hasNext() Возвращает true, если в вложенном списке еще остались целые числа, и false в противном случае.

Пример:
Input: nestedList = [[1,1],2,[1,1]]
Output: [1,1,2,1,1]
Explanation: By calling next repeatedly until hasNext returns false, the order of elements returned by next should be: [1,1,2,1,1].


👨‍💻 Алгоритм:

1⃣Инициализация
Сохраняйте исходный вложенный список в стеке или очереди. Используйте стек для сохранения состояния итерации по вложенным спискам.

2⃣Метод next()
Возвращает следующий целый элемент из стека или очереди. Если текущий элемент является списком, развёртывайте его и добавляйте элементы в стек.

3⃣Метод hasNext()
Проверяет, есть ли в стеке или очереди оставшиеся целые элементы. Если на вершине стека находится список, развёртывайте его до тех пор, пока не встретится целый элемент.

😎 Решение:
class NestedIterator(nestedList: List<NestedInteger>) : Iterator<Int> {
private val stack: Deque<NestedInteger> = ArrayDeque()

init {
flatten(nestedList)
}

private fun flatten(nestedList: List<NestedInteger>) {
for (ni in nestedList.reversed()) {
stack.push(ni)
}
}

override fun next(): Int {
return stack.pop().getInteger()
}

override fun hasNext(): Boolean {
while (stack.isNotEmpty() && !stack.peek().isInteger()) {
flatten(stack.pop().getList())
}
return stack.isNotEmpty()
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
#medium
Задача: 729. My Calendar I

Вы создаете программу для использования в качестве календаря. Мы можем добавить новое событие, если его добавление не приведет к двойному бронированию. Двойное бронирование происходит, когда два события имеют некоторое непустое пересечение (т.е, Событие можно представить в виде пары целых чисел start и end, которая представляет собой бронирование на полуоткрытом интервале [start, end), диапазоне вещественных чисел x таких, что start <= x < end. Реализация класса MyCalendar: MyCalendar() Инициализирует объект календаря. boolean book(int start, int end) Возвращает true, если событие может быть успешно добавлено в календарь, не вызывая двойного бронирования. В противном случае возвращается false и событие не добавляется в календарь.

Пример:
Input
["MyCalendar", "book", "book", "book"]
[[], [10, 20], [15, 25], [20, 30]]
Output
[null, true, false, true]


👨‍💻 Алгоритм:

1⃣Создайте класс MyCalendar с инициализатором для хранения списка событий.

2⃣Реализуйте метод book(int start, int end) для проверки пересечения нового события с уже существующими событиями.

3⃣Если новое событие не пересекается с существующими событиями, добавьте его в список событий и верните true. В противном случае верните false.

😎 Решение:
class MyCalendar() {
private val events = mutableListOf<Pair<Int, Int>>()

fun book(start: Int, end: Int): Boolean {
for ((s, e) in events) {
if (start < e && end > s) {
return false
}
}
events.add(Pair(start, end))
return true
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
#medium
Задача: 731. My Calendar II

Вы создаете программу для использования в качестве календаря. Мы можем добавить новое событие, если его добавление не приведет к тройному бронированию. Тройное бронирование происходит, когда три события имеют некоторое непустое пересечение (т.е, Событие можно представить в виде пары целых чисел start и end, которая представляет собой бронирование на полуоткрытом интервале [start, end), диапазоне вещественных чисел x таких, что start <= x < end. Реализация класса MyCalendarTwo: MyCalendarTwo() Инициализирует объект календаря. boolean book(int start, int end) Возвращает true, если событие может быть успешно добавлено в календарь, не вызывая тройного бронирования. В противном случае возвращается false и событие не добавляется в календарь.

Пример:
Input
["MyCalendarTwo", "book", "book", "book", "book", "book", "book"]
[[], [10, 20], [50, 60], [10, 40], [5, 15], [5, 10], [25, 55]]
Output
[null, true, true, true, false, true, true]


👨‍💻 Алгоритм:

1⃣Создайте два списка: один для отслеживания всех событий, второй для отслеживания пересечений. подпоследовательностей.

2⃣При добавлении нового события сначала проверьте, не пересекается ли оно с любыми существующими пересечениями.

3⃣Если пересечение не обнаружено, добавьте новое событие и обновите список пересечений при необходимости.

😎 Решение:
class MyCalendarTwo {
private val events = mutableListOf<Pair<Int, Int>>()
private val overlaps = mutableListOf<Pair<Int, Int>>()

fun book(start: Int, end: Int): Boolean {
for ((os, oe) in overlaps) {
if (start < oe && end > os) {
return false
}
}
for ((es, ee) in events) {
if (start < ee && end > es) {
overlaps.add(Pair(maxOf(start, es), minOf(end, ee)))
}
}
events.add(Pair(start, end))
return true
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
#medium
Задача: 737. Sentence Similarity II

Мы можем представить предложение в виде массива слов, например, предложение "I am happy with leetcode" можно представить как arr = ["I", "am",happy", "with", "leetcode"].

Даны два предложения sentence1 и sentence2, каждое из которых представлено в виде массива строк, и массив пар строк similarPairs, где similarPairs[i] = [xi, yi] указывает, что два слова xi и yi похожи. Возвращается true, если предложения sentence1 и sentence2 похожи, или false, если они не похожи. Два предложения похожи, если: у них одинаковая длина (т.е, Заметьте, что слово всегда похоже само на себя, также обратите внимание, что отношение сходства является транзитивным. Например, если слова a и b похожи, а слова b и c похожи, то a и c похожи.

Пример:
Input: sentence1 = ["great","acting","skills"], sentence2 = ["fine","drama","talent"], similarPairs = [["great","good"],["fine","good"],["drama","acting"],["skills","talent"]]
Output: true


👨‍💻 Алгоритм:

1⃣Проверить, одинаковой ли длины предложения sentence1 и sentence2. Если нет, вернуть false.

2⃣Построить граф схожести слов с использованием словаря.

3⃣Использовать поиск в глубину (DFS) для проверки транзитивной схожести слов в предложениях.

😎 Решение:
fun areSentencesSimilar(sentence1: Array<String>, sentence2: Array<String>, similarPairs: List<List<String>>): Boolean {
if (sentence1.size != sentence2.size) {
return false
}

val graph = mutableMapOf<String, MutableList<String>>()
for (pair in similarPairs) {
val (x, y) = pair
graph.computeIfAbsent(x) { mutableListOf() }.add(y)
graph.computeIfAbsent(y) { mutableListOf() }.add(x)
}

fun dfs(word1: String, word2: String, visited: MutableSet<String>): Boolean {
if (word1 == word2) {
return true
}
visited.add(word1)
for (neighbor in graph[word1].orEmpty()) {
if (neighbor !in visited && dfs(neighbor, word2, visited)) {
return true
}
}
return false
}

for (i in sentence1.indices) {
if (sentence1[i] != sentence2[i]) {
if (!dfs(sentence1[i], sentence2[i], mutableSetOf())) {
return false
}
}
}

return true
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
#medium
Задача: 738. Monotone Increasing Digits

Целое число имеет монотонно возрастающие цифры тогда и только тогда, когда каждая пара соседних цифр x и y удовлетворяет x <= y. Задав целое число n, верните наибольшее число, которое меньше или равно n с монотонно возрастающими цифрами.

Пример:
Input: n = 10
Output: 9


👨‍💻 Алгоритм:

1⃣Преобразуйте число в строку для удобства обработки.

2⃣Найдите позицию, где последовательность перестает быть монотонной.

3⃣Уменьшите соответствующую цифру и установите все последующие цифры в 9.

😎 Решение:
fun monotoneIncreasingDigits(N: Int): Int {
val digits = N.toString().toCharArray()
var marker = digits.size

for (i in digits.size - 1 downTo 1) {
if (digits[i] < digits[i - 1]) {
marker = i
digits[i - 1] = (digits[i - 1] - '0' - 1 + '0'.toInt()).toChar()
}
}

for (i in marker until digits.size) {
digits[i] = '9'
}

return String(digits).toInt()
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
#medium
Задача: 739. Daily Temperatures

Задав массив целых чисел temperature, представляющих дневные температуры, верните массив answer, такой, что answer[i] - это количество дней, которые нужно подождать после i-го дня, чтобы температура стала теплее. Если в будущем не существует дня, для которого это возможно, сохраните answer[i] == 0.

Пример:
Input: temperatures = [73,74,75,71,69,72,76,73]
Output: [1,1,4,2,1,1,0,0]


👨‍💻 Алгоритм:

1⃣Создайте стек для хранения индексов дней с температурами, для которых еще не найден более теплый день.

2⃣Пройдите по массиву температур и для каждого дня: Пока текущая температура больше температуры дня на вершине стека, обновляйте массив ответов и удаляйте вершину стека. Добавьте текущий день в стек.

3⃣Возвращайте массив ответов.

😎 Решение:
fun dailyTemperatures(T: IntArray): IntArray {
val answer = IntArray(T.size)
val stack = mutableListOf<Int>()

for (i in T.indices) {
while (stack.isNotEmpty() && T[i] > T[stack.last()]) {
val j = stack.removeAt(stack.size - 1)
answer[j] = i - j
}
stack.add(i)
}

return answer
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
#medium
Задача: 740. Delete and Earn

Вам дан целочисленный массив nums. Вы хотите максимизировать количество очков, выполнив следующую операцию любое количество раз: Выберите любой элемент nums[i] и удалите его, чтобы заработать nums[i] очков. После этого вы должны удалить каждый элемент, равный nums[i] - 1, и каждый элемент, равный nums[i] + 1. Верните максимальное количество очков, которое вы можете заработать, применив вышеуказанную операцию некоторое количество раз.

Пример:
Input: nums = [3,4,2]
Output: 6


👨‍💻 Алгоритм:

1⃣Подсчитайте количество каждого числа в массиве nums.

2⃣Используйте динамическое программирование для расчета максимальных очков, которые можно заработать, используя накопленный результат для чисел, меньших текущего. Добавьте текущий день в стек.

3⃣Для каждого числа num в nums, учитывайте два случая: не брать число или взять число и добавить его очки.

😎 Решение:
fun deleteAndEarn(nums: IntArray): Int {
val count = mutableMapOf<Int, Int>()
for (num in nums) {
count[num] = count.getOrDefault(num, 0) + 1
}

var avoid = 0
var using = 0
var prev = -1

for (num in count.keys.sorted()) {
if (num - 1 != prev) {
val newAvoid = maxOf(avoid, using)
using = num * count[num]!! + maxOf(avoid, using)
avoid = newAvoid
} else {
val newAvoid = maxOf(avoid, using)
using = num * count[num]!! + avoid
avoid = newAvoid
}
prev = num
}

return maxOf(avoid, using)
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
#medium
Задача: 742. Closest Leaf in a Binary Tree

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

Пример:
Input: root = [1,3,2], k = 1
Output: 2


👨‍💻 Алгоритм:

1⃣Пройдите дерево, чтобы найти путь от корня до узла k и сохранить его в список.

2⃣Найдите все листья и минимальное расстояние до них, используя BFS, начиная с найденного узла k.

3⃣Верните значение ближайшего листа.

😎 Решение:
class TreeNode(var `val`: Int) {
var left: TreeNode? = null
var right: TreeNode? = null
}

fun findClosestLeaf(root: TreeNode?, k: Int): Int {
val path = mutableListOf<TreeNode>()
val leaves = mutableMapOf<TreeNode, Int>()

fun findPath(node: TreeNode?, k: Int): Boolean {
if (node == null) return false
path.add(node)
if (node.`val` == k) return true
if (findPath(node.left, k) || findPath(node.right, k)) return true
path.removeAt(path.size - 1)
return false
}

fun findLeaves(node: TreeNode?) {
if (node == null) return
if (node.left == null && node.right == null) leaves[node] = 0
findLeaves(node.left)
findLeaves(node.right)
}

findPath(root, k)
findLeaves(root)

val queue = mutableListOf<Pair<TreeNode, Int>>()
queue.add(Pair(path.last(), 0))
val visited = mutableSetOf<TreeNode>()

while (queue.isNotEmpty()) {
val (node, dist) = queue.removeAt(0)
if (leaves.containsKey(node)) return node.`val`
visited.add(node)
node.left?.let {
if (!visited.contains(it)) queue.add(Pair(it, dist + 1))
}
node.right?.let {
if (!visited.contains(it)) queue.add(Pair(it, dist + 1))
}
if (path.size > 1) {
queue.add(Pair(path.removeAt(path.size - 1), dist + 1))
}
}
return -1
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
#medium
Задача: 743. Network Delay Time

Дана сеть из узлов, помеченных от 1 до n. Также дано times - список времен прохождения сигнала в виде направленных ребер times[i] = (ui, vi, wi), где ui - исходный узел, vi - целевой узел, а wi - время прохождения сигнала от источника до цели. Мы пошлем сигнал из заданного узла k. Верните минимальное время, которое потребуется всем узлам, чтобы получить сигнал. Если все узлы не могут получить сигнал, верните -1.

Пример:
Input: times = [[2,1,1],[2,3,1],[3,4,1]], n = 4, k = 2
Output: 2


👨‍💻 Алгоритм:

1⃣Представьте граф в виде списка смежности.

2⃣Используйте алгоритм Дейкстры для нахождения кратчайших путей от узла k до всех других узлов.

3⃣Найдите максимальное значение среди кратчайших путей к узлам. Если какой-либо узел недостижим, верните -1.

😎 Решение:
import java.util.*

fun networkDelayTime(times: Array<IntArray>, n: Int, k: Int): Int {
val graph = mutableMapOf<Int, MutableList<Pair<Int, Int>>>()
for (i in 1..n) {
graph[i] = mutableListOf()
}
for (time in times) {
graph[time[0]]?.add(Pair(time[1], time[2]))
}

val minHeap = PriorityQueue<Pair<Int, Int>>(compareBy { it.first })
minHeap.add(Pair(0, k))
val minTime = mutableMapOf<Int, Int>()
for (i in 1..n) {
minTime[i] = Int.MAX_VALUE
}
minTime[k] = 0

while (minHeap.isNotEmpty()) {
val (time, node) = minHeap.poll()
graph[node]?.forEach { (neighbor, t) ->
val newTime = time + t
if (newTime < minTime[neighbor]!!) {
minTime[neighbor] = newTime
minHeap.add(Pair(newTime, neighbor))
}
}
}

val maxTime = minTime.values.maxOrNull()!!
return if (maxTime == Int.MAX_VALUE) -1 else maxTime
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
#medium
Задача: 1530. Number of Good Leaf Nodes Pairs

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

Верните количество хороших пар листовых узлов в дереве.

Пример:
Input: root = [1,2,3,null,4], distance = 3
Output: 1
Explanation: The leaf nodes of the tree are 3 and 4 and the length of the shortest path between them is 3. This is the only good pair.


👨‍💻 Алгоритм:

1⃣ Инициализируйте список смежности для преобразования дерева в граф и множество для хранения листовых узлов. Используйте вспомогательный метод traverseTree для обхода дерева, чтобы построить граф и найти листовые узлы. В параметрах поддерживайте текущий узел, а также родительский узел. Если текущий узел является листом, добавьте его в множество. В списке смежности добавьте текущий узел в список соседей родительского узла и наоборот. Рекурсивно вызовите traverseTree для левого и правого дочернего узла текущего узла.

2⃣ Инициализируйте переменную ans для подсчета количества хороших пар листовых узлов. Итеративно переберите каждый листовой узел в множестве. Запустите BFS для текущего листового узла. BFS можно прервать досрочно, как только будут обнаружены все узлы, находящиеся на расстоянии от текущего листового узла. Увеличьте ans для каждого листового узла, найденного в каждом запуске BFS.

3⃣ Верните ans / 2. Мы считаем каждую пару дважды, поэтому нужно разделить на 2, чтобы получить фактическое количество.

😎 Решение:
class Solution {
fun countPairs(root: TreeNode?, distance: Int): Int {
val graph = mutableMapOf<TreeNode, MutableList<TreeNode>>()
val leafNodes = mutableSetOf<TreeNode>()
traverseTree(root, null, graph, leafNodes)
var ans = 0
for (leaf in leafNodes) {
val bfsQueue: Queue<TreeNode> = LinkedList()
val seen = mutableSetOf<TreeNode>()
bfsQueue.add(leaf)
seen.add(leaf)
for (i in 0..distance) {
val size = bfsQueue.size
for (j in 0 until size) {
val currNode = bfsQueue.poll()
if (leafNodes.contains(currNode) && currNode != leaf) {
ans++
}
graph[currNode]?.let {
for (neighbor in it) {
if (!seen.contains(neighbor)) {
bfsQueue.add(neighbor)
seen.add(neighbor)
}
}
}
}
}
}
return ans / 2
}

private fun traverseTree(currNode: TreeNode?, prevNode: TreeNode?,
graph: MutableMap<TreeNode, MutableList<TreeNode>>, leafNodes: MutableSet<TreeNode>) {
if (currNode == null) {
return
}
if (currNode.left == null && currNode.right == null) {
leafNodes.add(currNode)
}
if (prevNode != null) {
graph.computeIfAbsent(prevNode) { mutableListOf() }.add(currNode)
graph.computeIfAbsent(currNode) { mutableListOf() }.add(prevNode)
}
traverseTree(currNode.left, currNode, graph, leafNodes)
traverseTree(currNode.right, currNode, graph, leafNodes)
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤯1
#medium
Задача: 750. Number Of Corner Rectangles

Дан указатель на начало односвязного списка и два целых числа left и right, где left <= right. Необходимо перевернуть узлы списка, начиная с позиции left и заканчивая позицией right, и вернуть измененный список.

Пример:
Input: grid = [[1,0,0,1,0],[0,0,1,0,1],[0,0,0,1,0],[1,0,1,0,1]]
Output: 1


👨‍💻 Алгоритм:

1⃣Пройдите по строкам матрицы. Для каждой пары строк, найдите все столбцы, где оба значения равны 1.

2⃣Подсчитайте количество таких столбцов. Если их больше одного, то они образуют прямоугольники.

3⃣Для каждой пары строк добавьте количество возможных прямоугольников в общий счетчик.

😎 Решение:
fun countCornerRectangles(grid: Array<IntArray>): Int {
var count = 0
for (i in grid.indices) {
for (j in i + 1 until grid.size) {
var numPairs = 0
for (k in grid[0].indices) {
if (grid[i][k] == 1 && grid[j][k] == 1) {
numPairs++
}
}
if (numPairs > 1) {
count += numPairs * (numPairs - 1) / 2
}
}
}
return count
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
#medium
Задача: 751. IP to CIDR

Дан указатель на начало односвязного списка и два целых числа 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
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
#medium
Задача: 752. Open the Lock

Перед вами замок с 4 круглыми колесами. Каждое колесо имеет 10 слотов: '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'. Колеса могут свободно вращаться и оборачиваться: например, мы можем повернуть "9" так, чтобы получился "0", или "0" так, чтобы получился "9". Каждый ход состоит из поворота одного колеса на один слот. Изначально замок начинается с '0000', строки, представляющей состояние 4 колес. Вам дан список тупиков, то есть если замок отобразит любой из этих кодов, колеса замка перестанут вращаться, и вы не сможете его открыть. Учитывая цель, представляющую значение колес, которое позволит отпереть замок, верните минимальное общее количество оборотов, необходимое для открытия замка, или -1, если это невозможно.

Пример:
Input: deadends = ["0201","0101","0102","1212","2002"], target = "0202"
Output: 6


👨‍💻 Алгоритм:

1⃣Используйте алгоритм BFS для поиска кратчайшего пути от начального состояния '0000' до целевого состояния, избегая тупиков. Инициализируйте очередь с начальным состоянием '0000' и начальным шагом 0. Используйте множество для отслеживания посещенных состояний, чтобы избежать повторного посещения одного и того же состояния.

2⃣Для каждого состояния в очереди: Проверьте все возможные переходы на следующий шаг, вращая каждое колесо на +1 и -1. Если найденное состояние является целевым, верните количество шагов. Если найденное состояние не является тупиком и не было посещено ранее, добавьте его в очередь и отметьте как посещенное.

3⃣Если очередь пуста и целевое состояние не найдено, верните -1.

😎 Решение:
fun openLock(deadends: Array<String>, target: String): Int {
fun neighbors(node: String): List<String> {
val res = mutableListOf<String>()
val nodeArray = node.toCharArray()
for (i in 0 until 4) {
val x = nodeArray[i].toString().toInt()
for (d in listOf(-1, 1)) {
val y = (x + d + 10) % 10
nodeArray[i] = y.toString().toCharArray()[0]
res.add(String(nodeArray))
}
nodeArray[i] = x.toString().toCharArray()[0]
}
return res
}

val dead = deadends.toSet()
val queue = ArrayDeque<Pair<String, Int>>()
queue.add("0000" to 0)
val visited = mutableSetOf("0000")

while (queue.isNotEmpty()) {
val (node, steps) = queue.removeFirst()
if (node == target) return steps
if (node in dead) continue
for (neighbor in neighbors(node)) {
if (neighbor !in visited) {
visited.add(neighbor)
queue.add(neighbor to steps + 1)
}
}
}

return -1
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
#medium
Задача: 753. Cracking the Safe

Имеется сейф, защищенный паролем. Пароль представляет собой последовательность из n цифр, каждая из которых может находиться в диапазоне [0, k - 1]. Сейф имеет особый способ проверки пароля. Например, правильный пароль - "345", а вы вводите "012345": после ввода 0 последние 3 цифры - "0", что неверно. После ввода 1 последние 3 цифры - "01", что неверно. После ввода 2 последние 3 цифры - "012", что неверно.
После ввода 3 последние 3 цифры - "123", что неверно. После ввода 4 последние 3 цифры - "234", что неверно. После ввода 5 последние 3 цифры - "345", что верно, и сейф разблокируется. Верните любую строку минимальной длины, которая разблокирует сейф на определенном этапе ввода.

Пример:
Input: n = 1, k = 2
Output: "10"


👨‍💻 Алгоритм:

1⃣Создайте граф, где каждая вершина представляет собой строку длины n-1, а каждое ребро между двумя вершинами представляет собой добавление одной из цифр из диапазона [0, k-1].

2⃣Используйте алгоритм Эйлерова пути или цикла для нахождения пути, который проходит через каждое ребро ровно один раз.

3⃣Составьте итоговую строку, которая включает начальную вершину и все добавленные цифры.

😎 Решение:
fun crackSafe(n: Int, k: Int): String {
val seen = mutableSetOf<String>()
val result = StringBuilder()

fun dfs(node: String) {
for (x in 0 until k) {
val neighbor = node + x
if (!seen.contains(neighbor)) {
seen.add(neighbor)
dfs(neighbor.drop(1))
result.append(x)
}
}
}

val startNode = "0".repeat(n - 1)
dfs(startNode)

result.append(startNode)
return result.toString()
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
1
#medium
Задача: 754. Reach a Number

Вы стоите в позиции 0 на бесконечной числовой прямой. В позиции target находится пункт назначения. Вы можете сделать некоторое количество ходов numMoves так, чтобы: на каждом ходу вы могли пойти либо налево, либо направо. Во время i-го хода (начиная с i == 1 до i == numMoves) вы делаете i шагов в выбранном направлении. Учитывая целое число target, верните минимальное количество ходов (т.е. минимальное numMoves), необходимое для достижения пункта назначения.

Пример:
Input: target = 2
Output: 3


👨‍💻 Алгоритм:

1⃣Инициализируйте переменную для текущей позиции (position) и счетчик шагов (steps).

2⃣Используйте цикл, чтобы добавлять к position текущее количество шагов и увеличивать steps.

3⃣Если position достигает или превышает target и разница между position и target четная, остановите цикл и верните steps.

😎 Решение:
fun reachTarget(target: Int): Int {
var target = kotlin.math.abs(target)
var position = 0
var steps = 0
while (position < target || (position - target) % 2 != 0) {
steps++
position += steps
}
return steps
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
#medium
Задача: 755. Pour Water

Вам дана карта высот, представленная в виде целочисленного массива heights, где heights[i] - высота местности в точке i. Ширина в каждой точке равна 1. Вам также даны два целых числа volume и k. Единицы объема воды будут падать в точке k. Вода сначала падает в точке k и упирается в самую высокую местность или воду в этой точке. Затем она течет по следующим правилам: если капля в конечном итоге упадет, двигаясь влево, то двигайтесь влево. Иначе, если капля в конечном итоге упадет, двигаясь вправо, то двигайтесь вправо. Иначе поднимайтесь в текущее положение. Здесь "в конечном итоге упадет" означает, что капля в конечном итоге окажется на более низком уровне, если она будет двигаться в этом направлении. Кроме того, уровень означает высоту местности плюс вода в этом столбе. Мы можем предположить, что на двух сторонах, выходящих за пределы массива, есть бесконечно высокая местность. Также не может быть частичного равномерного распределения воды более чем на один блок сетки, и каждая единица воды должна находиться ровно в одном блоке.

Пример:
Input: heights = [2,1,1,2,1,2,2], volume = 4, k = 3
Output: [2,2,2,3,2,2,2]


👨‍💻 Алгоритм:

1⃣Инициализируйте цикл для добавления объема воды.

2⃣Для каждой единицы воды: Проверьте, может ли вода двигаться влево и упасть на более низкий уровень. Если нет, проверьте, может ли вода двигаться вправо и упасть на более низкий уровень. Если нет, добавьте воду в текущую позицию.

3⃣Повторите шаг 2, пока не будет добавлен весь объем воды.

😎 Решение:
fun pourWater(heights: IntArray, volume: Int, k: Int): IntArray {
for (v in 0 until volume) {
var dropIndex = k
for (d in listOf(-1, 1)) {
var i = k
while (i + d in heights.indices && heights[i + d] <= heights[i]) {
if (heights[i + d] < heights[i]) {
dropIndex = i + d
}
i += d
}
if (dropIndex != k) break
}
heights[dropIndex]++
}
return heights
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
#medium
Задача: 756. Pyramid Transition Matrix

Вы складываете блоки так, чтобы получилась пирамида. Каждый блок имеет цвет, который представлен одной буквой. Каждый ряд блоков содержит на один блок меньше, чем ряд под ним, и располагается по центру сверху. Чтобы пирамида выглядела эстетично, допускаются только определенные треугольные узоры. Треугольный узор состоит из одного блока, уложенного поверх двух блоков. Шаблоны задаются в виде списка допустимых трехбуквенных строк, где первые два символа шаблона представляют левый и правый нижние блоки соответственно, а третий символ - верхний блок. Например, "ABC" представляет треугольный шаблон с блоком 'C', уложенным поверх блоков 'A' (слева) и 'B' (справа). Обратите внимание, что это отличается от "BAC", где "B" находится слева внизу, а "A" - справа внизу. Вы начинаете с нижнего ряда блоков bottom, заданного в виде одной строки, который вы должны использовать в качестве основания пирамиды. Учитывая bottom и allowed, верните true, если вы можете построить пирамиду до самой вершины так, чтобы каждый треугольный узор в пирамиде был в allowed, или false в противном случае.

Пример:
Input: bottom = "BCD", allowed = ["BCC","CDE","CEA","FFF"]
Output: true


👨‍💻 Алгоритм:

1⃣Создайте структуру данных для хранения допустимых треугольных узоров.

2⃣Напишите рекурсивную функцию, которая проверяет возможность построения следующего уровня пирамиды.

3⃣Начните с нижнего уровня пирамиды и используйте рекурсию для построения каждого следующего уровня, проверяя каждый треугольный узор на допустимость.

😎 Решение:
class Solution {
fun pyramidTransition(bottom: String, allowed: List<String>): Boolean {
val allowedDict = mutableMapOf<String, MutableList<Char>>()

for (pattern in allowed) {
val key = pattern.substring(0, 2)
val value = pattern[2]
allowedDict.computeIfAbsent(key) { mutableListOf() }.add(value)
}

return canBuild(bottom, allowedDict)
}

private fun canBuild(currentLevel: String, allowedDict: Map<String, List<Char>>): Boolean {
if (currentLevel.length == 1) return true

val nextLevelOptions = mutableListOf<List<Char>>()
for (i in 0 until currentLevel.length - 1) {
val key = currentLevel.substring(i, i + 2)
if (!allowedDict.containsKey(key)) return false
nextLevelOptions.add(allowedDict[key]!!)
}

return canBuildNextLevel(nextLevelOptions, 0, "", allowedDict)
}

private fun canBuildNextLevel(options: List<List<Char>>, index: Int, nextLevel: String, allowedDict: Map<String, List<Char>>): Boolean {
if (index == options.size) return canBuild(nextLevel, allowedDict)
for (option in options[index]) {
if (canBuildNextLevel(options, index + 1, nextLevel + option, allowedDict)) return true
}
return false
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
#medium
Задача: 758. Bold Words in String

При наличии массива ключевых слов и строки a выделите все ключевые слова [i] жирным шрифтом. Все буквы между тегами <b> и </b> выделяются жирным шрифтом.

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

Пример:
Input: words = ["ab","bc"], s = "aabcd"
Output: "a<b>abc</b>d"


👨‍💻 Алгоритм:

1⃣Создайте массив для хранения флагов, указывающих, какие символы в строке a должны быть выделены жирным шрифтом.

2⃣Пройдите по каждому ключевому слову и отметьте соответствующие позиции в массиве флагов.

3⃣Постройте результирующую строку, добавляя теги <b> и </b> на основе массива флагов.

😎 Решение:
fun addBoldTags(keywords: Array<String>, s: String): String {
val n = s.length
val bold = BooleanArray(n)

for (word in keywords) {
var start = s.indexOf(word)
while (start != -1) {
for (i in start until start + word.length) {
bold[i] = true
}
start = s.indexOf(word, start + 1)
}
}

val result = StringBuilder()
var i = 0
while (i < n) {
if (bold[i]) {
result.append("<b>")
while (i < n && bold[i]) {
result.append(s[i])
i++
}
result.append("</b>")
} else {
result.append(s[i])
i++
}
}

return result.toString()
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
#medium
Задача: 763. Partition Labels

Вам дана строка s. Мы хотим разбить строку на как можно больше частей так, чтобы каждая буква встречалась не более чем в одной части. Обратите внимание, что разбиение выполняется так, чтобы после конкатенации всех частей по порядку получилась строка s. Верните список целых чисел, представляющих размер этих частей.

Пример:
Input: s = "ababcbacadefegdehijhklij"
Output: [9,7,8]


👨‍💻 Алгоритм:

1⃣Создайте словарь для хранения последней позиции каждой буквы в строке.

2⃣Пройдите по строке, отслеживая максимальную позицию текущей части.

3⃣Когда текущая позиция совпадает с максимальной позицией, завершите часть и начните новую.

😎 Решение:
fun partitionLabels(s: String): List<Int> {
val lastPos = IntArray(26)
for (i in s.indices) {
lastPos[s[i] - 'a'] = i
}

val partitions = mutableListOf<Int>()
var j = 0
var anchor = 0
for (i in s.indices) {
j = maxOf(j, lastPos[s[i] - 'a'])
if (i == j) {
partitions.add(i - anchor + 1)
anchor = i + 1
}
}
return partitions
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM