uk
Feedback
Swift | LeetCode

Swift | LeetCode

Відкрити в Telegram

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

Показати більше
1 311
Підписники
Немає даних24 години
-27 днів
-1130 днів

Триває завантаження даних...

Залучення підписників
вересень '26
вересень '26
+4
в 0 каналах
серпень '26
+9
в 0 каналах
Get PRO
липень '26
+3
в 0 каналах
Get PRO
червень '26
+4
в 1 каналах
Get PRO
травень '26
+4
в 0 каналах
Get PRO
квітень '26
+6
в 0 каналах
Get PRO
березень '26
+4
в 0 каналах
Get PRO
лютий '26
+9
в 0 каналах
Get PRO
січень '26
+17
в 0 каналах
Get PRO
грудень '25
+12
в 0 каналах
Get PRO
листопад '25
+54
в 0 каналах
Get PRO
жовтень '25
+30
в 0 каналах
Get PRO
вересень '25
+32
в 0 каналах
Get PRO
серпень '25
+37
в 0 каналах
Get PRO
липень '25
+34
в 1 каналах
Get PRO
червень '25
+30
в 0 каналах
Get PRO
травень '25
+33
в 0 каналах
Get PRO
квітень '25
+68
в 0 каналах
Get PRO
березень '25
+143
в 2 каналах
Get PRO
лютий '25
+113
в 1 каналах
Get PRO
січень '25
+113
в 53 каналах
Get PRO
грудень '24
+50
в 0 каналах
Get PRO
листопад '24
+55
в 0 каналах
Get PRO
жовтень '24
+143
в 12 каналах
Get PRO
вересень '24
+459
в 331 каналах
Get PRO
серпень '24
+87
в 0 каналах
Get PRO
липень '24
+325
в 219 каналах
Get PRO
червень '24
+261
в 232 каналах
Дата
Залучення підписників
Згадування
Канали
15 вересня+1
14 вересня0
13 вересня0
12 вересня0
11 вересня+1
10 вересня0
09 вересня0
08 вересня0
07 вересня+1
06 вересня0
05 вересня0
04 вересня0
03 вересня0
02 вересня+1
01 вересня0
Дописи каналу
Задача: 124. Binary Tree Maximum Path Sum Сложность: hard Вам дан массив цен, где prices[i] — это цена данной акции в i-й день. Найдите максимальную прибыль, которую вы можете получить. Вы можете совершить не более двух транзакций. Пример:
Input: prices = [3,3,5,0,0,3,1,4]
Output: 6
Explanation: Buy on day 4 (price = 0) and sell on day 6 (price = 3), profit = 3-0 = 3.
Then buy on day 7 (price = 1) and sell on day 8 (price = 4), profit = 4-1
👨‍💻 Алгоритм: 1⃣Наивная реализация этой идеи заключалась бы в разделении последовательностей на две части и последующем перечислении каждой из подпоследовательностей, хотя это определенно не самое оптимизированное решение. Для последовательности длиной N у нас было бы N возможных разделений (включая отсутствие разделения), каждый элемент был бы посещен один раз в каждом разделении. В результате общая временная сложность этой наивной реализации составила бы O(N²). 2⃣Мы могли бы сделать лучше, чем наивная реализация O(N²). Что касается алгоритмов разделяй и властвуй, одна из общих техник, которую мы можем применить для оптимизации временной сложности, называется динамическим программированием (DP), где мы меняем менее повторяющиеся вычисления на некоторое дополнительное пространство. В алгоритмах динамического программирования обычно мы создаем массив одного или двух измерений для сохранения промежуточных оптимальных результатов. В данной задаче мы бы использовали два массива, один массив сохранял бы результаты последовательности слева направо, а другой массив сохранял бы результаты последовательности справа налево. Для удобства мы могли бы назвать это двунаправленным динамическим программированием. 3⃣Сначала мы обозначаем последовательность цен как Prices[i], с индексом начиная от 0 до N-1. Затем мы определяем два массива, а именно left_profits[i] и right_profits[i]. Как следует из названия, каждый элемент в массиве left_profits[i] будет содержать максимальную прибыль, которую можно получить от выполнения одной транзакции в левой подпоследовательности цен от индекса ноль до i, (т.е. Prices[0], Prices[1], ... Prices[i]). Например, для подпоследовательности [7, 1, 5] соответствующий left_profits[2] будет равен 4, что означает покупку по цене 1 и продажу по цене 5. И каждый элемент в массиве right_profits[i] будет содержать максимальную прибыль, которую можно получить от выполнения одной транзакции в правой подпоследовательности цен от индекса i до N-1, (т.е. Prices[i], Prices[i+1], ... Prices[N-1]). Например, для правой подпоследовательности [3, 6, 4] соответствующий right_profits[3] будет равен 3, что означает покупку по цене 3 и продажу по цене 6. Теперь, если мы разделим последовательность цен вокруг элемента с индексом i на две подпоследовательности, с левыми подпоследовательностями как Prices[0], Prices[1], ... Prices[i] и правой подпоследовательностью как Prices[i+1], ... Prices[N-1], то общая максимальная прибыль, которую мы получим от этого разделения (обозначенная как max_profits[i]), может быть выражена следующим образом: max_profits[i] = left_profits[i] + right_profits[i+1] 😎 Решение:
class Solution {
    func maxProfit(_ prices: [Int]) -> Int {
        if prices.count <= 1 {
            return 0
        }

        let length = prices.count
        var leftMin = prices[0]
        var rightMax = prices.last!

        var leftProfits = Array(repeating: 0, count: length)
        var rightProfits = Array(repeating: 0, count: length + 1)

        for l in 1..<length {
            leftProfits[l] = max(leftProfits[l - 1], prices[l] - leftMin)
            leftMin = min(leftMin, prices[l])

            let r = length - 1 - l
            rightProfits[r] = max(rightProfits[r + 1], rightMax - prices[r])
            rightMax = max(rightMax, prices[r])
        }

        var maxProfit = 0
        for i in 0..<length {
            maxProfit = max(maxProfit, leftProfits[i] + rightProfits[i + 1])
        }

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

2
Задача: 60. Permutation Sequence Сложность: hard Множество [1, 2, 3, ..., n] содержит в общей сложности n! уникальных перестановок. Списком и маркировкой всех перестановок по порядку, мы получаем следующую последовательность для n = 3: "123" "132" "213" "231" "312" "321" Дано n и k, верните k-ю перестановку последовательности. Пример: Input: n = 3, k = 3 Output: "213" 👨‍💻 Алгоритм: 1⃣Сгенерируйте входной массив nums чисел от 1 до N. 2⃣Вычислите все факториальные основы от 0 до (N−1)!. 3⃣Уменьшите k на 1, чтобы значение попало в интервал (0, N!−1). 😎 Решение: class Solution { func getPermutation(_ n: Int, _ k: Int) -> String { var factorials = [Int](repeating: 1, count: n) var nums = [Character]() for i in 1..<n { factorials[i] = factorials[i - 1] * i nums.append(Character(String(i + 1))) } var k = k - 1 var result = "" for i in stride(from: n - 1, through: 0, by: -1) { let idx = k / factorials[i] k -= idx * factorials[i] result.append(nums[idx]) nums.remove(at: idx) } return result } } Ставь 👍 и забирай 📚 Базу знаний
44
3
Задача: 127. Word Ladder Сложность: Hard Секвенция трансформации от слова beginWord к слову endWord с использованием словаря wordList представляет собой последовательность слов beginWord -> s1 -> s2 -> ... -> sk, при которой: Каждая пара соседних слов отличается ровно одной буквой. Каждый элемент si для 1 <= i <= k присутствует в wordList. Отметим, что beginWord не обязан быть в wordList. sk равно endWord. Для двух слов, beginWord и endWord, и словаря wordList, верните количество слов в кратчайшей секвенции трансформации от beginWord к endWord, или 0, если такая секвенция не существует. Пример: Input: beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log","cog"] Output: 5 Explanation: One shortest transformation sequence is "hit" -> "hot" -> "dot" -> "dog" -> cog", which is 5 words long. 👨‍💻 Алгоритм: 1⃣Препроцессинг списка слов: Осуществите препроцессинг заданного списка слов (wordList), чтобы найти все возможные промежуточные состояния слов. Сохраните эти состояния в словаре, где ключом будет промежуточное слово, а значением — список слов, имеющих то же промежуточное состояние. 2⃣Использование очереди для обхода: Поместите в очередь кортеж, содержащий beginWord и число 1, где 1 обозначает уровень узла. Вам нужно вернуть уровень узла endWord, так как он будет представлять длину кратчайшей последовательности преобразования. Используйте словарь посещений, чтобы избежать циклов. 3⃣Поиск кратчайшего пути через BFS (обход в ширину): Пока в очереди есть элементы, получите первый элемент очереди. Для каждого слова определите все промежуточные преобразования и проверьте, не являются ли эти преобразования также преобразованиями других слов из списка. Для каждого найденного слова, которое имеет общее промежуточное состояние с текущим словом, добавьте в очередь пару (слово, уровень + 1), где уровень — это уровень текущего слова. Если вы достигли искомого слова, его уровень покажет длину кратчайшей последовательности преобразования. 😎 Решение: import Foundation class Solution { func ladderLength(_ beginWord: String, _ endWord: String, _ wordList: [String]) -> Int { let L = beginWord.count var allComboDict: [String: [String]] = [:] wordList.forEach { word in for i in 0..<L { let newWord = "\(word.prefix(i))*\(word.suffix(L - i - 1))" var transformations = allComboDict[newWord, default: []] transformations.append(word) allComboDict[newWord] = transformations } } var queue: [(String, Int)] = [(beginWord, 1)] var visited: [String: Bool] = [beginWord: true] while !queue.isEmpty { let (word, level) = queue.removeFirst() for i in 0..<L { let newWord = "\(word.prefix(i))*\(word.suffix(L - i - 1))" if let adjacentWords = allComboDict[newWord] { for adjacentWord in adjacentWords { if adjacentWord == endWord { return level + 1 } if visited[adjacentWord] != true { visited[adjacentWord] = true queue.append((adjacentWord, level + 1)) } } } } } return 0 } } Ставь 👍 и забирай 📚 Базу знаний
44
4
Задача: 1406. Stone Game III Сложность: hard Алиса и Боб продолжают свои игры с кучами камней. Камни расположены в ряд, и каждый камень имеет ассоциированное значение, которое представлено целым числом в массиве stoneValue. Алиса и Боб ходят по очереди, начиная с Алисы. В свой ход каждый игрок может взять 1, 2 или 3 камня из первых оставшихся камней в ряду. Счет каждого игрока равен сумме значений взятых камней. Изначально счет каждого игрока равен 0. Цель игры — закончить с наивысшим счетом, и победителем становится игрок с наивысшим счетом, при этом возможна ничья. Игра продолжается, пока все камни не будут взяты. Предположим, что Алиса и Боб играют оптимально. Верните "Alice", если Алиса выиграет, "Bob", если выиграет Боб, или "Tie", если они закончат игру с одинаковым счетом. Пример: Input: stoneValue = [1,2,3,7] Output: "Bob" Explanation: Alice will always lose. Her best move will be to take three piles and the score become 6. Now the score of Bob is 7 and Bob wins. 👨‍💻 Алгоритм: 1⃣Инициализируйте массив dp размером n+1 и установите dp[n] в 0. 2⃣Итеративно обновляйте dp[i] для всех i от n-1 до 0, вычисляя максимальную разницу в баллах, которые могут получить игроки при оптимальной игре. 3⃣Определите победителя, сравнивая dp[0] с 0: если больше, победит Алиса; если меньше, победит Боб; если равно, будет ничья. 😎 Решение: class Solution { func stoneGameIII(_ stoneValue: [Int]) -> String { let n = stoneValue.count var dp = [Int](repeating: 0, count: n + 1) for i in stride(from: n - 1, through: 0, by: -1) { dp[i] = stoneValue[i] - dp[i + 1] if i + 2 <= n { dp[i] = max(dp[i], stoneValue[i] + stoneValue[i + 1] - dp[i + 2]) } if i + 3 <= n { dp[i] = max(dp[i], stoneValue[i] + stoneValue[i + 1] + stoneValue[i + 2] - dp[i + 3]) } } if dp[0] > 0 { return "Alice" } else if dp[0] < 0 { return "Bob" } else { return "Tie" } } } Ставь 👍 и забирай 📚 Базу знаний
54
5
Задача: 1380. Lucky Numbers in a Matrix Сложность: easy Дана матрица m x n из различных чисел, верните все счастливые числа в матрице в любом порядке. Счастливое число — это элемент матрицы, который является минимальным элементом в своей строке и максимальным в своем столбце. Пример: Input: matrix = [[3,7,8],[9,11,13],[15,16,17]] Output: [15] Explanation: 15 is the only lucky number since it is the minimum in its row and the maximum in its column. 👨‍💻 Алгоритм: 1⃣Сохраните минимум каждой строки в список rowMin и максимум каждого столбца в список colMax. 2⃣Итерируйте по каждому числу в матрице и проверяйте, равно ли оно rowMin[i] и colMax[j]. 3⃣Если число удовлетворяет условию, добавьте его в список luckyNumbers и верните luckyNumbers. 😎 Решение: class Solution { func luckyNumbers (_ matrix: [[Int]]) -> [Int] { let N = matrix.count let M = matrix[0].count var rowMin = [Int]() for i in 0..<N { var rMin = Int.max for j in 0..<M { rMin = min(rMin, matrix[i][j]) } rowMin.append(rMin) } var colMax = [Int]() for i in 0..<M { var cMax = Int.min for j in 0..<N { cMax = max(cMax, matrix[j][i]) } colMax.append(cMax) } var luckyNumbers = [Int]() for i in 0..<N { for j in 0..<M { if matrix[i][j] == rowMin[i] && matrix[i][j] == colMax[j] { luckyNumbers.append(matrix[i][j]) } } } return luckyNumbers } } Ставь 👍 и забирай 📚 Базу знаний
71
6
Задача: 153. Find Minimum in Rotated Sorted Array Сложность: Medium Предположим, что массив длиной n, отсортированный в порядке возрастания, повернут от 1 до n раз. Например, массив nums = [0,1,2,4,5,6,7] может стать: [4,5,6,7,0,1,2], если он был повернут 4 раза. [0,1,2,4,5,6,7], если он был повернут 7 раз. Обратите внимание, что поворот массива [a[0], a[1], a[2], ..., a[n-1]] 1 раз приводит к массиву [a[n-1], a[0], a[1], a[2], ..., a[n-2]]. Для данного отсортированного и повернутого массива nums с уникальными элементами верните минимальный элемент этого массива. Вы должны написать алгоритм, который работает за время O(log n). Пример: Input: nums = [3,4,5,1,2] Output: 1 Explanation: The original array was [1,2,3,4,5] rotated 3 times. 👨‍💻 Алгоритм: 1⃣Нахождение середины массива: Определите элемент, находящийся посередине массива. 2⃣Определение направления поиска: Если элемент в середине больше первого элемента массива, это означает, что точка перегиба (минимальный элемент) находится справа от середины. Если элемент в середине меньше первого элемента массива, это указывает на то, что точка перегиба находится слева от середины. 3⃣Остановка поиска при нахождении точки перегиба: Поиск прекращается, когда найдена точка перегиба, когда выполняется одно из двух условий: nums[mid] > nums[mid + 1] – следовательно, mid+1 является наименьшим элементом. nums[mid - 1] > nums[mid] – следовательно, mid является наименьшим элементом. 😎 Решение: class Solution { func findMin(_ nums: [Int]) -> Int { if nums.count == 1 { return nums[0] } var left = 0 var right = nums.count - 1 if nums[right] > nums[0] { return nums[0] } while left <= right { let mid = left + (right - left) / 2 if nums[mid] > nums[mid + 1] { return nums[mid + 1] } if mid > 0 && nums[mid - 1] > nums[mid] { return nums[mid] } if nums[mid] > nums[0] { left = mid + 1 } else { right = mid - 1 } } return -1 } } Ставь 👍 и забирай 📚 Базу знаний
64
7
Задача: 847. Shortest Path Visiting All Nodes Сложность: hard У вас есть неориентированный связный граф из n узлов, пронумерованных от 0 до n - 1. Вам дан массив graph, где graph[i] — это список всех узлов, соединенных с узлом i ребром. Верните длину кратчайшего пути, который посещает каждый узел. Вы можете начать и закончить в любом узле, вы можете несколько раз посещать узлы и использовать ребра повторно. Пример: Input: graph = [[1],[0,2,4],[1,3,4],[2],[1,2]] Output: 4 Explanation: One possible path is [0,1,4,2,3] 👨‍💻 Алгоритм: 1⃣Если граф содержит только один узел, верните 0, так как мы можем начать и закончить в этом узле, не делая никаких шагов. 2⃣Инициализируйте необходимые переменные: количество узлов n, маску окончания endingMask, структуру данных seen для предотвращения циклов, очередь для выполнения BFS и счетчик шагов steps. 3⃣Заполните очередь и seen начальными состояниями (начало в каждом узле с маской, указывающей, что посещен только данный узел), затем выполните BFS для поиска кратчайшего пути, который посещает все узлы. Если найден путь, возвращайте количество шагов. 😎 Решение: class Solution { func shortestPathLength(_ graph: [[Int]]) -> Int { let n = graph.count if n == 1 { return 0 } let endingMask = (1 << n) - 1 var seen = Array(repeating: Array(repeating: false, count: endingMask), count: n) var queue = [(node: Int, mask: Int)]() for i in 0..<n { queue.append((i, 1 << i)) seen[i][1 << i] = true } var steps = 0 while !queue.isEmpty { var nextQueue = [(node: Int, mask: Int)]() for (node, mask) in queue { for neighbor in graph[node] { let nextMask = mask | (1 << neighbor) if nextMask == endingMask { return 1 + steps } if !seen[neighbor][nextMask] { seen[neighbor][nextMask] = true nextQueue.append((neighbor, nextMask)) } } } steps += 1 queue = nextQueue } return -1 } } Ставь 👍 и забирай 📚 Базу знаний
55
8
Задача: 284. Peeking Iterator Сложность: medium Создайте итератор, который поддерживает операцию peek (просмотр следующего элемента) на существующем итераторе, помимо операций hasNext (проверка наличия следующего элемента) и next (получение следующего элемента). Реализуйте класс PeekingIterator: PeekingIterator(Iterator<int> nums): Инициализирует объект с заданным итератором целых чисел. int next(): Возвращает следующий элемент в массиве и перемещает указатель на следующий элемент. boolean hasNext(): Возвращает true, если в массиве еще есть элементы. int peek(): Возвращает следующий элемент в массиве без перемещения указателя. Пример: Input ["PeekingIterator", "next", "peek", "next", "next", "hasNext"] [[[1, 2, 3]], [], [], [], [], []] Output [null, 1, 2, 2, 3, false] Explanation PeekingIterator peekingIterator = new PeekingIterator([1, 2, 3]); // [1,2,3] peekingIterator.next(); // return 1, the pointer moves to the next element [1,2,3]. peekingIterator.peek(); // return 2, the pointer does not move [1,2,3]. peekingIterator.next(); // return 2, the pointer moves to the next element [1,2,3] peekingIterator.next(); // return 3, the pointer moves to the next element [1,2,3] peekingIterator.hasNext(); // return False 👨‍💻 Алгоритм: 1⃣Инициализация итератора: В конструкторе класса PeekingIterator инициализируйте итератор и проверьте, есть ли следующий элемент. Если есть, установите его как next, иначе установите next в null. 2⃣Операция peek: Метод peek возвращает значение next, не перемещая указатель итератора. 3⃣Операции next и hasNext: Метод next возвращает текущее значение next, обновляет next к следующему элементу в итераторе и перемещает указатель итератора. Если нет следующего элемента, бросает исключение NoSuchElementException. Метод hasNext возвращает true, если next не равно null, и false в противном случае. 😎 Решение: class PeekingIterator { private var iterator: IndexingIterator<[Int]> private var nextElement: Int? init(_ nums: [Int]) { self.iterator = nums.makeIterator() self.nextElement = self.iterator.next() } func next() -> Int { let currentElement = nextElement nextElement = iterator.next() return currentElement! } func hasNext() -> Bool { return nextElement != nil } func peek() -> Int { return nextElement! } } Ставь 👍 и забирай 📚 Базу знаний
59
9
Задача: 1357. Apply Discount Every n Orders Сложность: medium В супермаркете, который посещает множество покупателей, товары представлены двумя параллельными массивами целых чисел products и prices, где i-й товар имеет идентификатор products[i] и цену prices[i]. Когда покупатель оплачивает товар, его счет представлен двумя параллельными массивами целых чисел product и amount, где j-й приобретенный товар имеет идентификатор product[j], а amount[j] - количество купленного товара. Их промежуточный итог рассчитывается как сумма каждого amount[j] * (цена j-го товара). Супермаркет решил провести распродажу. Каждому n-му покупателю, оплачивающему свои покупки, будет предоставлена скидка в процентах. Сумма скидки задается параметром discount, и покупатель получит скидку в discount процентов от своего промежуточного итога. Формально, если их промежуточный итог составляет bill, то они фактически заплатят bill * ((100 - discount) / 100). Реализуйте класс Cashier: Cashier(int n, int discount, int[] products, int[] prices): инициализирует объект с параметрами n, discount, а также массивами товаров и их цен. double getBill(int[] product, int[] amount): возвращает итоговую сумму счета с примененной скидкой (если применима). Ответы, отличающиеся от фактического значения не более чем на 10^-5, будут приняты. Пример: Input ["Cashier","getBill","getBill","getBill","getBill","getBill","getBill","getBill"] [[3,50,[1,2,3,4,5,6,7],[100,200,300,400,300,200,100]],[[1,2],[1,2]],[[3,7],[10,10]],[[1,2,3,4,5,6,7],[1,1,1,1,1,1,1]],[[4],[10]],[[7,3],[10,10]],[[7,5,3,1,6,4,2],[10,10,10,9,9,9,7]],[[2,3,5],[5,3,2]]] Output [null,500.0,4000.0,800.0,4000.0,4000.0,7350.0,2500.0] 👨‍💻 Алгоритм: 1⃣Инициализация объекта: Создайте класс Cashier с конструктором, который принимает параметры n, discount, products и prices. В конструкторе инициализируйте необходимые переменные и создайте словарь для сопоставления идентификаторов продуктов с их ценами. 2⃣Обработка каждого счета: Создайте метод getBill, который принимает массивы product и amount. Вычислите промежуточный итог счета, умножая количество каждого продукта на его цену и суммируя результаты. Увеличьте счетчик клиентов. Если клиент является n-м по счету, примените скидку к промежуточному итогу. 3⃣Верните итоговую сумму счета. 😎 Решение: class Cashier { private let n: Int private let discount: Int private var productsPrices: [Int: Int] private var customerCount: Int init(_ n: Int, _ discount: Int, _ products: [Int], _ prices: [Int]) { self.n = n self.discount = discount self.customerCount = 0 self.productsPrices = Dictionary(uniqueKeysWithValues: zip(products, prices)) } func getBill(_ product: [Int], _ amount: [Int]) -> Double { customerCount += 1 var bill = 0.0 for (index, prod) in product.enumerated() { bill += Double(productsPrices[prod]! * amount[index]) } if customerCount % n == 0 { bill *= Double(100 - discount) / 100.0 } return bill } } Ставь 👍 и забирай 📚 Базу знаний
65
10
Задача: 165. Compare Version Numbers Сложность: medium Даны две строки версий, version1 и version2. Сравните их. Строка версии состоит из ревизий, разделенных точками '.'. Значение ревизии — это её целочисленное преобразование с игнорированием ведущих нулей. Для сравнения строк версий сравнивайте их значения ревизий в порядке слева направо. Если одна из строк версий имеет меньше ревизий, то отсутствующие значения ревизий следует считать равными 0. Верните следующее: - Если version1 < version2, верните -1. - Если version1 > version2, верните 1. - В противном случае верните 0. Пример: Input: version1 = "1.2", version2 = "1.10" Output: -1 Explanation: version1's second revision is "2" and version2's second revision is "10": 2 < 10, so version1 < version2. 👨‍💻 Алгоритм: 1⃣Разделение строк: Разделите обе строки по символу точки на два массива. 2⃣Итерация и сравнение: Итерируйте по самому длинному массиву и сравнивайте элементы по одному. Если один из массивов закончился, предполагайте, что все оставшиеся элементы в другом массиве равны нулю, чтобы продолжить сравнение с более длинной строкой. 3⃣Определение результатов сравнения: Если два сегмента не равны, верните 1 или -1 в зависимости от того, какой сегмент больше. Если все сегменты равны после завершения цикла, версии считаются равными. Верните 0 😎 Решение: class Solution { func compareVersion(_ version1: String, _ version2: String) -> Int { let tokens1 = version1.split(separator: ".").map { Int($0)! } let tokens2 = version2.split(separator: ".").map { Int($0)! } for i in 0..<max(tokens1.count, tokens2.count) { let i1 = i < tokens1.count ? tokens1[i] : 0 let i2 = i < tokens2.count ? tokens2[i] : 0 if i1 != i2 { return i1 > i2 ? 1 : -1 } } return 0 } } Ставь 👍 и забирай 📚 Базу знаний
68
11
Задача: 1133. Largest Unique Number Сложность: easy Вам Дан целочисленный массив nums, верните наибольшее целое число, которое встречается только один раз. Если ни одно целое число не встречается один раз, верните -1. Пример: Input: nums = [5,7,3,9,4,9,8,3,1] Output: 8 Explanation: The maximum integer in the array is 9 but it is repeated. The number 8 occurs only once, so it is the answer. 👨‍💻 Алгоритм: 1⃣Создайте хеш-таблицу для хранения количества каждого числа в массиве. 2⃣Пройдите по массиву и заполните хеш-таблицу количеством каждого числа. 3⃣Инициализируйте результат значением -1. Пройдите по хеш-таблице и если значение ключа равно 1, установите результат равным максимальному значению между ключом и текущим результатом. Верните результат. 😎 Решение: class Solution { func largestUniqueNumber(_ nums: [Int]) -> Int { var count = [Int: Int]() for num in nums { count[num, default: 0] += 1 } var result = -1 for (key, value) in count { if value == 1 { result = max(result, key) } } return result } } Ставь 👍 и забирай 📚 Базу знаний
76
12
Задача: 678. Valid Parenthesis String Сложность: medium Создайте карту, которая позволяет выполнять следующие действия: Отображает строковый ключ на заданное значение. Возвращает сумму значений, у которых ключ имеет префикс, равный заданной строке. Реализуйте класс MapSum: Дана строка s, содержащая только три типа символов: '(', ')' и '*'. Вернуть true, если s является допустимой. Следующие правила определяют допустимую строку: Любая открывающая скобка '(' должна иметь соответствующую закрывающую скобку ')'. Любая закрывающая скобка ')' должна иметь соответствующую открывающую скобку '('. Открывающая скобка '(' должна идти перед соответствующей закрывающей скобкой ')'. '*' может рассматриваться как одна закрывающая скобка ')', одна открывающая скобка '(' или пустая строка "". Пример: Input: s = "()" Output: true Example 2: 👨‍💻 Алгоритм: 1⃣Инициализировать 2D вектор memo размером s.size() x s.size() - 1, представляющий неинициализированное состояние. Вызвать вспомогательную функцию isValidString с начальными параметрами index = 0, openCount = 0 и строкой s. Вернуть результат isValidString. 2⃣Вспомогательная функция isValidString. Базовый случай: если index достиг конца строки (index == s.size.), вернуть true, если openCount равен 0 (все скобки сбалансированы), и false в противном случае. Проверить, был ли результат для текущего index и openCount уже вычислен (мемоизирован) в memo. Если да, вернуть мемоизированный результат. Инициализировать isValid как false. Если текущий символ s[index] равен '*': Попробовать трактовать '*' как '(' и вызвать isValidString рекурсивно с index + 1 и openCount + 1. Если рекурсивный вызов вернет true, обновить isValid на true. Если openCount не равен нулю, попробовать трактовать '*' как ')' и вызвать isValidString рекурсивно с index + 1 и openCount - 1. Если рекурсивный вызов вернет true, обновить isValid на true. Попробовать трактовать '*' как пустой символ и вызвать isValidString рекурсивно с index + 1 и тем же openCount. Если рекурсивный вызов вернет true, обновить isValid на true. 3⃣Продолжение функции isValidString. Если текущий символ s[index] равен '(': Вызвать isValidString рекурсивно с index + 1 и openCount + 1. Обновить isValid с результатом рекурсивного вызова. Если текущий символ s[index] равен ')': Если openCount не равен нулю (есть открытые скобки), вызвать isValidString рекурсивно с index + 1 и openCount - 1. Обновить isValid с результатом рекурсивного вызова. Мемоизировать результат isValid в memo[index][openCount]. Вернуть isValid. 😎 Решение: class Solution { func checkValidString(_ s: String) -> Bool { var memo = Array(repeating: Array(repeating: -1, count: s.count), count: s.count) return isValidString(0, 0, s, &memo) } private func isValidString(_ index: Int, _ openCount: Int, _ str: String, _ memo: inout [[Int]]) -> Bool { if index == str.count { return openCount == 0 } if memo[index][openCount] != -1 { return memo[index][openCount] == 1 } var isValid = false let currentIndex = str.index(str.startIndex, offsetBy: index) if str[currentIndex] == "*" { isValid = isValidString(index + 1, openCount + 1, str, &memo) if openCount > 0 { isValid = isValid || isValidString(index + 1, openCount - 1, str, &memo) } isValid = isValid || isValidString(index + 1, openCount, str, &memo) } else if str[currentIndex] == "(" { isValid = isValidString(index + 1, openCount + 1, str, &memo) } else if openCount > 0 { isValid = isValidString(index + 1, openCount - 1, str, &memo) } memo[index][openCount] = isValid ? 1 : 0 return isValid } } Ставь 👍 и забирай 📚 Базу знаний
70
13
Задача: 1066. Campus Bikes II Сложность: medium На кампусе, представленном в виде двумерной сетки, есть n рабочих и m велосипедов, где n <= m. Каждый рабочий и велосипед имеют координаты на этой сетке. Мы назначаем каждому рабочему уникальный велосипед таким образом, чтобы сумма Манхэттенских расстояний между каждым рабочим и назначенным ему велосипедом была минимальной. Верните минимально возможную сумму Манхэттенских расстояний между каждым рабочим и назначенным ему велосипедом. Манхэттенское расстояние между двумя точками p1 и p2 вычисляется как Manhattan(p1, p2) = |p1.x - p2.x| + |p1.y - p2.y|. Пример: Input: text = "thestoryofleetcodeandme", words = ["story","fleet","leetcode"] Output: [[3,7],[9,13],[10,17]] 👨‍💻 Алгоритм: 1⃣Для каждого рабочего, начиная с рабочего с индексом 0, пройдите по всем велосипедам и назначьте велосипед рабочему, если он доступен (visited[bikeIndex] = false). После назначения велосипеда отметьте его как недоступный (visited[bikeIndex] = true). Добавьте Манхэттенское расстояние от этого назначения к общей текущей сумме расстояний, представленной currDistanceSum, и выполните рекурсивный вызов для следующего рабочего. 2⃣Когда рекурсивный вызов завершится, сделайте велосипед снова доступным, установив visited[bikeIndex] в false. Если мы назначили велосипеды всем рабочим, сравните currDistanceSum с smallestDistanceSum и обновите smallestDistanceSum соответственно. 3⃣Перед назначением любого велосипеда рабочему, проверьте, если currDistanceSum уже больше или равен smallestDistanceSum. Если это так, пропустите остальных рабочих и вернитесь. Это связано с тем, что currDistanceSum может только увеличиваться, и таким образом мы не найдем лучший результат, чем smallestDistanceSum, используя текущую комбинацию рабочих и велосипедов. 😎 Решение: class Solution { function indexPairs($text, $words) { $wordsSet = array_flip($words); $ans = []; for ($i = 0; $i < strlen($text); $i++) { for ($j = $i; $j < strlen($text); $j++) { if (isset($wordsSet[substr($text, $i, $j - $i + 1)])) { $ans[] = [$i, $j]; } } } return $ans; } } Ставь 👍 и забирай 📚 Базу знаний
67
14
Задача: 1103. Distribute Candies to People Сложность: easy Мы распределяем некоторое количество конфет ряду из n = num_people человек следующим образом: Сначала даем 1 конфету первому человеку, 2 конфеты второму человеку и так далее, пока не дадим n конфет последнему человеку. Затем мы возвращаемся к началу ряда, давая n + 1 конфету первому человеку, n + 2 конфеты второму человеку и так далее, пока не дадим 2 * n конфет последнему человеку. Этот процесс повторяется (мы каждый раз даем на одну конфету больше и возвращаемся к началу ряда после достижения конца), пока у нас не закончатся конфеты. Последний человек получит все оставшиеся конфеты (не обязательно на одну больше, чем в предыдущий раз). Верните массив (длиной num_people и суммой candies), который представляет собой окончательное распределение конфет. Пример: Input: candies = 7, num_people = 4 Output: [1,2,3,1] Explanation: On the first turn, ans[0] += 1, and the array is [1,0,0,0]. On the second turn, ans[1] += 2, and the array is [1,2,0,0]. On the third turn, ans[2] += 3, and the array is [1,2,3,0]. On the fourth turn, ans[3] += 1 (because there is only one candy left), and the final array is [1,2,3,1]. 👨‍💻 Алгоритм: 1⃣Вычислите количество людей, получивших полные подарки, и оставшиеся конфеты: p = floor(sqrt(2C+0.25)-0.5) remainig = C - p(p+1)/2 2⃣Вычислите количество полных циклов и распределите конфеты: rows = p // n d[i]= i*rows + n*rows*(rows-1)/2 3⃣Добавьте конфеты за дополнительный неполный цикл и оставшиеся конфеты: d[i]+=i+n⋅rows для первых p%n людей d[p%n]+=remaining Верните распределение конфет d 😎 Решение: class Solution { func distributeCandies(_ candies: Int, _ num_people: Int) -> [Int] { let n = num_people let p = Int((sqrt(2 * Double(candies) + 0.25) - 0.5)) let remaining = candies - (p + 1) * p / 2 let rows = p / n let cols = p % n var d = [Int](repeating: 0, count: n) for i in 0..<n { d[i] = (i + 1) * rows + (rows * (rows - 1) / 2) * n if i < cols { d[i] += i + 1 + rows * n } } d[cols] += remaining return d } } Ставь 👍 и забирай 📚 Базу знаний
65
15
Задача: 941. Valid Mountain Array Сложность: easy Задав массив целых чисел arr, верните true тогда и только тогда, когда он является допустимым горным массивом. Напомним, что arr является горным массивом тогда и только тогда, когда: arr.length >= 3 Существует некоторое i с 0 < i < arr.length - 1 такое, что: arr[0] < arr[1] < ... < arr[i - 1] < arr[i] arr[i] > arr[i + 1] > ... > arr[arr.length - 1] Пример: Input: arr = [2,1] Output: false 👨‍💻 Алгоритм: 1⃣Убедиться, что длина массива не меньше 3. 2⃣Найти вершину горы, которая удовлетворяет условиям горного массива. Проверить, что все элементы слева от вершины строго возрастают. Проверить, что все элементы справа от вершины строго убывают. 3⃣Вернуть true, если оба условия выполнены, иначе вернуть false. 😎 Решение: class Solution { func validMountainArray(_ arr: [Int]) -> Bool { if arr.count < 3 { return false } var i = 1 while i < arr.count && arr[i] > arr[i - 1] { i += 1 } if i == 1 || i == arr.count { return false } while i < arr.count && arr[i] < arr[i - 1] { i += 1 } return i == arr.count } } Ставь 👍 и забирай 📚 Базу знаний
86
16
🚨60 минут Пожизненный PRO-доступ на easyoffer (подготовка к IT-собесам + поиск оффера) по цене одного года закрывается прямо сейчас. Один платёж — доступ навсегда. Последнее напоминание 👇 👉 https://easyoffer.ru/pro
14
17
⚠️ 3 часа до конца акции. Последний шанс забрать пожизненный PRO на easyoffer по цене одного года. Это полный доступ к подготовке к собесам и инструментам поиска работы (вопросы с реальных интервью, ответы сеньоров, автоотклики, тренажёры) — один раз и навсегда, вместо ежегодной оплаты. В полночь цена возвращается к обычной. 👉 https://easyoffer.ru/pro
20
18
⏳ Ребята, сегодня заканчивается акция, о которой стоит знать, если вы в поиске работы или планируете сменить её в ближайший год. easyoffer — это платформа для подготовки к IT-собесам и поиска оффера. Внутри: – база реальных вопросов и live-coding задач с собесов (с частотой их встречаемости) – эталонные ответы от Senior-разработчиков – 1100+ записей настоящих интервью (Сбер, Яндекс, Авито, WB, OZON, МТС) – автоотклики на hh, генератор резюме под вакансию, тренажёры собеседований Сегодня пожизненный PRO-доступ продаётся по цене одного года — платишь один раз и пользуешься всем этим всю жизнь, включая будущие фичи. С завтрашнего дня — только обычная годовая подписка. 👉 https://easyoffer.ru/pro
56
19
Пожизненный PRO — по цене одного года. Покупаешь один раз — пользуешься всю жизнь: 👉 https://easyoffer.ru/pro 🚀 PRO-доступ закроет 99% проблем на пути к офферу: 1. 1100+ записей реальных собеседований (включая топы: Сбер, Авито, Яндекс, WB, OZON, МТС). Видите всё изнутри: как спрашивают, как отвечают сильные кандидаты и на каких ошибках проваливаются 80%. 2. База live-coding задач и вопросов с реальных собесов — с уникальной системой вероятности их встречи. Готовитесь не вслепую, а точечно по темам, которые спрашивают чаще всего. 3. Эталонные ответы от Senior-разработчиков. Никакой воды и догадок — только чёткие структурированные решения, за которые дают «зелёный свет» к офферу. 4. Полный доступ ко всем грейдам и профессиям. Junior вы или Senior, тестировщик, разработчик или проджект — вы получаете ВСЕ материалы easyoffer без ограничений. Безлимитно, Все, Навсегда. 5. База 400+ тестовых заданий. Прокачивайте навыки на реальных задачах — тех самых, что дают перед собесом. 6. Автоотклики на hh.ru — пока вы спите, резюме уходит рекрутерам автоматически. Экономия сотен часов ручного кликанья. 7. Аналитика ТОП-требований из вакансий. Парсим рынок и показываем, какие скиллы сейчас в цене. Апгрейдите резюме точечно и проходите ATS-фильтры (они отсеивают до 75% резюме ещё до рекрутера). 8. Генератор резюме и CV под каждую вакансию. Забудьте про «универсальное» резюме — нейросеть адаптирует ваш опыт под конкретную позицию за минуту и повышает шансы на приглашение в разы. 9. Тренажёры подготовки к собеседованию: «Реальное собеседование» — сценарий вопросов из настоящих интервью. «Проработка вопросов» — флеш-карточки по методике интервальных повторений (как Anki) 10. 🔥 Самое важное: все будущие фичи Вы платите один раз, а продукт растёт всю жизнь. Каждое обновление, каждый новый инструмент, каждая фича, которая появится за все годы проекта, автоматически падает вам в подписку без доплат. Вы фиксируете цену года, а получаете продукт, который через пару лет будет стоить в разы дороже ⭐️ Это уникальная акция пока сайт в режиме Beta. Успей ей воспользоваться⏳ Завтра последний день. 👉 https://easyoffer.ru/pro
47
20
🔥 Осталось 3 дня! Пожизненный easyoffer PRO по цене одного года. Покупаешь один раз – пользуешься всю жизнь. Что входит в PRO: – Вопросы и задачи с реальных собеседований в конкретных компаниях – Лучшие ответы и видео-примеры от middle/senior специалистов – Записи реальных собеседований – Обход фильтров ATS с топ-30 ключевых слов в резюме – Автоотклики на hh – Тренажёры и симуляторы для идеальной подготовки к интервью ⏳ Акция действует только до 2 сентября 23:59 по МСК 👉 Забрать PRO со скидкой 70%: https://easyoffer.ru/pro
62