Swift | LeetCode
1.49K subscribers
126 photos
1.06K links
Cайт easyoffer.ru
Реклама @easyoffer_adv
ВП @easyoffer_vp

Тесты t.iss.one/+bn3i_aLL0-A2ZGMy
Вопросы собесов t.iss.one/+wtkjBoN6OI5hNGEy
Вакансии t.iss.one/+3o9-Ytdiv_E5OGIy
Download Telegram
#hard
Задача: 632. Smallest Range Covering Elements from K Lists

У вас есть k списков отсортированных целых чисел в неубывающем порядке. Найдите наименьший диапазон, в который входит хотя бы одно число из каждого из k списков. Мы определяем, что диапазон [a, b] меньше диапазона [c, d], если b - a < d - c или a < c, если b - a == d - c.

Пример:
Input: nums = [[4,10,15,24,26],[0,9,12,20],[5,18,22,30]]
Output: [20,24]


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

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

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

3⃣Проверка и обновление диапазона
Продолжайте обновлять кучу и диапазон, пока возможно. Завершите, когда один из списков исчерпан.

😎 Решение:
import Foundation

struct Element: Comparable {
let value: Int
let row: Int
let col: Int

static func < (lhs: Element, rhs: Element) -> Bool {
return lhs.value < rhs.value
}
}

class Solution {
func smallestRange(_ nums: [[Int]]) -> [Int] {
var minHeap = [Element]()
var maxValue = Int.min

for i in 0..<nums.count {
let element = Element(value: nums[i][0], row: i, col: 0)
minHeap.append(element)
maxValue = max(maxValue, nums[i][0])
}

minHeap.sort()

var rangeStart = 0
var rangeEnd = Int.max

while minHeap.count == nums.count {
let minElement = minHeap.removeFirst()
if maxValue - minElement.value < rangeEnd - rangeStart {
rangeStart = minElement.value
rangeEnd = maxValue
}

if minElement.col + 1 < nums[minElement.row].count {
let newValue = nums[minElement.row][minElement.col + 1]
let newElement = Element(value: newValue, row: minElement.row, col: minElement.col + 1)
minHeap.append(newElement)
maxValue = max(maxValue, newValue)
minHeap.sort()
}
}

return [rangeStart, rangeEnd]
}
}


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

Супер некрасивое число — это положительное целое число, простые множители которого находятся в массиве primes.

Дано целое число n и массив целых чисел primes. Верните n-е супер некрасивое число.

Гарантируется, что n-е супер некрасивое число помещается в 32-битное знаковое целое число.

Пример:
Input: n = 12, primes = [2,7,13,19]
Output: 32
Explanation: [1,2,4,7,8,13,14,16,19,26,28,32] is the sequence of the first 12 super ugly numbers given primes = [2,7,13,19].


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

1⃣Инициализация
Создайте массив ugly_numbers длиной n для хранения супер некрасивых чисел. Создайте массив indices длиной primes для отслеживания позиций в массиве ugly_numbers. Создайте массив next_ugly длиной primes для хранения следующего возможного супер некрасивого числа для каждого простого числа из primes.

2⃣Генерация супер некрасивых чисел
Установите первое значение в ugly_numbers как 1. Повторяйте до тех пор, пока не заполните массив ugly_numbers: Найдите минимальное значение в массиве next_ugly и добавьте его в ugly_numbers. Обновите соответствующий индекс в indices и пересчитайте значение в next_ugly.

3⃣Возврат результата
Верните последнее значение в массиве ugly_numbers, которое будет n-м супер некрасивым числом.

😎 Решение:
class Solution {
func nthSuperUglyNumber(_ n: Int, _ primes: [Int]) -> Int {
var ugly_numbers = [1]
var indices = [Int](repeating: 0, count: primes.count)
var next_ugly = primes

for _ in 1..<n {
let next_val = next_ugly.min()!
ugly_numbers.append(next_val)

for i in 0..<primes.count {
if next_val == next_ugly[i] {
indices[i] += 1
next_ugly[i] = ugly_numbers[indices[i]] * primes[i]
}
}
}

return ugly_numbers.last!
}
}


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

Учитывая целое неотрицательное число c, решите, существуют ли два целых числа a и b такие, что a2 + b2 = c.

Пример:
Input: c = 5
Output: true


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

1⃣Проверка границ
Проверьте, если c меньше 0, немедленно верните false, так как сумма квадратов двух целых чисел не может быть отрицательной.

2⃣Инициализация указателей
Используйте два указателя a и b. Инициализируйте a на 0 и b на значение квадратного корня из c. Поиск решения: Используйте цикл для поиска a и b, таких что a^2 + b^2 == c: Если a^2 + b^2 равно c, верните true. Если a^2 + b^2 меньше c, увеличьте a на 1. Если a^2 + b^2 больше c, уменьшите b на 1.

3⃣Возвращение результата
Если цикл завершится без нахождения подходящих a и b, верните false.

😎 Решение:
import Foundation

func judgeSquareSum(_ c: Int) -> Bool {
var a = 0
var b = Int(sqrt(Double(c)))

while a <= b {
let total = a * a + b * b
if total == c {
return true
} else if total < c {
a += 1
} else {
b -= 1
}
}

return false
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
#medium
Задача: 634. Find the Derangement of An Array

В комбинаторной математике отклонение - это перестановка элементов множества таким образом, что ни один элемент не оказывается на прежнем месте. Вам дано целое число n. Изначально имеется массив, состоящий из n целых чисел от 1 до n в порядке возрастания, верните количество отклонений, которые он может породить. Поскольку ответ может быть огромным, верните его по модулю 109 + 7.

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


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

1⃣Инициализация массива для хранения результатов
Создайте массив dp для хранения количества отклонений для каждого значения от 0 до n. Установите начальные значения: dp[0] = 1 и dp[1] = 0.

2⃣Вычисление количества отклонений
Используйте динамическое программирование для вычисления количества отклонений для каждого значения от 2 до n. Формула для вычисления: dp[i] = (i - 1) * (dp[i - 1] + dp[i - 2]) % MOD.

3⃣Возвращение результата
Верните значение dp[n], которое будет количеством отклонений для n элементов, по модулю 10^9 + 7.

😎 Решение:
import Foundation

func countDerangements(_ n: Int) -> Int {
let MOD = 1_000_000_007
if n == 0 {
return 1
}
if n == 1 {
return 0
}

var dp = [Int](repeating: 0, count: n + 1)
dp[0] = 1
dp[1] = 0

for i in 2...n {
dp[i] = (i - 1) * (dp[i - 1] + dp[i - 2]) % MOD
}

return dp[n]
}


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

Вам дается несколько журналов, где каждый журнал содержит уникальный идентификатор и временную метку. Временная метка - это строка, имеющая следующий формат: Год:Месяц:День:Час:Минута:Секунда, например, 2017:01:01:23:59:59. Все домены - десятичные числа с нулевым добавлением. Реализация класса LogSystem: LogSystem() Инициализирует объект LogSystem. void put(int id, string timestamp) Сохраняет заданный журнал (id, timestamp) в вашей системе хранения.
int[] retrieve(string start, string end, string granularity) Возвращает идентификаторы журналов, временные метки которых находятся в диапазоне от start до end включительно. start и end имеют тот же формат, что и timestamp, а granularity означает, насколько точным должен быть диапазон (т. е. с точностью до дня, минуты и т. д.). Например, start = "2017:01:01:23:59:59", end = "2017:01:02:23:59:59", а granularity = "Day" означает, что нам нужно найти журналы в диапазоне от 1 января 2017 года до 2 января 2017 года включительно, а час, минуту и секунду для каждой записи журнала можно игнорировать.

Пример:
Input
["LogSystem", "put", "put", "put", "retrieve", "retrieve"]
[[], [1, "2017:01:01:23:59:59"], [2, "2017:01:01:22:59:59"], [3, "2016:01:01:00:00:00"], ["2016:01:01:01:01:01", "2017:01:01:23:00:00", "Year"], ["2016:01:01:01:01:01", "2017:01:01:23:00:00", "Hour"]]
Output
[null, null, null, null, [3, 2, 1], [2, 1]]


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

1⃣Инициализация и хранение журналов
Реализуйте метод put, который будет сохранять журнал с заданным id и timestamp в системе хранения.

2⃣Формирование диапазона
Реализуйте метод retrieve, который будет формировать диапазон временных меток на основе заданного start, end и granularity.

3⃣Фильтрация и возврат результатов
Используйте сформированный диапазон для фильтрации журналов и возврата идентификаторов тех журналов, чьи временные метки попадают в этот диапазон.

😎 Решение:
class LogSystem {
private var logs: [(Int, String)]

init() {
logs = []
}

func put(_ id: Int, _ timestamp: String) {
logs.append((id, timestamp))
}

func retrieve(_ start: String, _ end: String, _ granularity: String) -> [Int] {
let index = getGranularityIndex(granularity)
let startPrefix = String(start.prefix(index))
let endPrefix = String(end.prefix(index))

var result = [Int]()
for (id, timestamp) in logs {
let timestampPrefix = String(timestamp.prefix(index))
if startPrefix <= timestampPrefix && timestampPrefix <= endPrefix {
result.append(id)
}
}
return result
}

private func getGranularityIndex(_ granularity: String) -> Int {
switch granularity {
case "Year": return 4
case "Month": return 7
case "Day": return 10
case "Hour": return 13
case "Minute": return 16
case "Second": return 19
default: return 19
}
}
}


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

На однопоточном процессоре выполняется программа, содержащая n функций. Каждая функция имеет уникальный ID от 0 до n-1. Вызовы функций хранятся в стеке вызовов: когда начинается вызов функции, ее ID заталкивается в стек, а когда вызов функции заканчивается, ее ID выгружается из стека. Функция, чей идентификатор находится в верхней части стека, является текущей выполняемой функцией. Каждый раз, когда функция запускается или завершается, мы пишем лог с идентификатором, началом или завершением и меткой времени. Вам предоставляется список logs, где logs[i] представляет собой i-е сообщение лога, отформатированное как строка "{function_id}:{"start" | "end"}:{timestamp}". Например, "0:start:3" означает, что вызов функции с идентификатором 0 начался в начале временной метки 3, а "1:end:2" означает, что вызов функции с идентификатором 1 завершился в конце временной метки 2. Обратите внимание, что функция может быть вызвана несколько раз, возможно, рекурсивно. Исключительное время функции - это сумма времен выполнения всех вызовов функции в программе. Например, если функция вызывается дважды, причем один вызов выполняется за 2 единицы времени, а другой - за 1 единицу, то эксклюзивное время равно 2 + 1 = 3. Верните эксклюзивное время каждой функции в массив, где значение по i-му индексу представляет собой эксклюзивное время для функции с идентификатором i.

Пример:
Input: n = 2, logs = ["0:start:0","1:start:2","1:end:5","0:end:6"]
Output: [3,4]


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

1⃣Парсинг логов: Пройдитесь по каждому логу, чтобы распознать действие (start или end) и идентификатор функции вместе с временной меткой.

2⃣Использование стека: Используйте стек для отслеживания текущих вызовов функций. Если лог содержит start, добавьте функцию в стек и начните отсчет времени. Если лог содержит end, снимите функцию со стека и обновите эксклюзивное время.

3⃣Обновление времени выполнения: Когда функция завершает выполнение, обновите ее эксклюзивное время и также учитывайте время выполнения вложенных функций.

😎 Решение:
class Solution {
func exclusiveTime(_ n: Int, _ logs: [String]) -> [Int] {
var stack = [Int]()
var times = [Int](repeating: 0, count: n)
var prevTime = 0

for log in logs {
let parts = log.split(separator: ":")
let fid = Int(parts[0])!
let type = String(parts[1])
let time = Int(parts[2])!

if type == "start" {
if let last = stack.last {
times[last] += time - prevTime
}
stack.append(fid)
prevTime = time
} else {
times[stack.removeLast()] += time - prevTime + 1
prevTime = time + 1
}
}

return times
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
#easy
Задача: 637. Average of Levels in Binary Tree

Учитывая корень бинарного дерева, верните среднее значение узлов на каждом уровне в виде массива. Принимаются ответы в пределах 10-5 от фактического ответа.

Пример:
Input: root = [3,9,20,null,null,15,7]
Output: [3.00000,14.50000,11.00000]


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

1⃣Обход дерева: Используйте обход в ширину (BFS) для обхода каждого уровня дерева.

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

3⃣Сохранение результата: Сохраните среднее значение каждого уровня в массив и верните его.

😎 Решение:
public class TreeNode {
public var val: Int
public var left: TreeNode?
public var right: TreeNode?
public init() { self.val = 0; self.left = nil; self.right = nil; }
public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; }
public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) {
self.val = val
self.left = left
self.right = right
}
}

class Solution {
func averageOfLevels(_ root: TreeNode?) -> [Double] {
guard let root = root else { return [] }

var result = [Double]()
var queue: [TreeNode] = [root]

while !queue.isEmpty {
var levelSum = 0
let levelCount = queue.count
for _ in 0..<levelCount {
let node = queue.removeFirst()
levelSum += node.val
if let left = node.left { queue.append(left) }
if let right = node.right { queue.append(right) }
}
result.append(Double(levelSum) / Double(levelCount))
}

return result
}
}


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

В магазине LeetCode Store есть n предметов для продажи. Каждый товар имеет свою цену. Однако существуют специальные предложения, и специальное предложение состоит из одного или нескольких различных видов товаров с распродажной ценой. Вам дан целочисленный массив price, где price[i] - цена i-го товара, и целочисленный массив needs, где needs[i] - количество штук i-го товара, который вы хотите купить. Вам также дан массив special, где special[i] имеет размер n + 1, где special[i][j] - количество штук j-го товара в i-м предложении, а special[i][n] (т.е., Возвращает наименьшую цену, которую вы можете заплатить за определенный товар из заданных, где вы могли бы оптимально использовать специальные предложения. Вам не разрешается покупать больше товаров, чем вы хотите, даже если это снизит общую цену. Вы можете использовать любое из специальных предложений столько раз, сколько захотите.

Пример:
Input: price = [2,5], special = [[3,0,5],[1,2,10]], needs = [3,2]
Output: 14


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

1⃣Рекурсивное вычисление стоимости: Определите функцию, которая рекурсивно вычисляет минимальную стоимость для оставшихся нужд, используя динамическое программирование для запоминания уже вычисленных значений.

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

3⃣Выбор минимальной стоимости: Сравните стоимость при использовании специальных предложений и стоимость при покупке товаров по индивидуальным ценам, выбирая минимальную стоимость.

😎 Решение:
class Solution {
func shoppingOffers(_ price: [Int], _ special: [[Int]], _ needs: [Int]) -> Int {
var memo = [String: Int]()
return dfs(price, special, needs, &memo)
}

private func dfs(_ price: [Int], _ special: [[Int]], _ needs: [Int], _ memo: inout [String: Int]) -> Int {
let key = needs.map(String.init).joined(separator: ",")
if let cached = memo[key] {
return cached
}

var minPrice = zip(price, needs).map(*).reduce(0, +)

for offer in special {
var newNeeds = [Int]()
var valid = true
for i in 0..<needs.count {
if needs[i] < offer[i] {
valid = false
break
}
newNeeds.append(needs[i] - offer[i])
}
if valid {
minPrice = min(minPrice, offer.last! + dfs(price, special, newNeeds, &memo))
}
}

memo[key] = minPrice
return minPrice
}
}


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

Сообщение, содержащее буквы от A-Z, может быть закодировано в цифры с помощью следующего отображения: 'A' -> "1" 'B' -> "2" ... 'Z' -> "26" Чтобы декодировать закодированное сообщение, все цифры должны быть сгруппированы, а затем снова преобразованы в буквы с помощью обратного отображения (может быть несколько способов). Например, "11106" может быть преобразовано в: "AAJF" с группировкой (1 1 10 6) "KJF" с группировкой (11 10 6) Обратите внимание, что группировка (1 11 06) недействительна, поскольку "06" не может быть преобразовано в "F", так как "6" отличается от "06". В дополнение к вышеуказанным преобразованиям кодированное сообщение может содержать символ "*", который может представлять любую цифру от "1" до "9" ("0" исключается). Например, кодированное сообщение "1*" может представлять любое из кодированных сообщений "11", "12", "13", "14", "15", "16", "17", "18" или "19". Декодирование "1*" эквивалентно декодированию любого из кодированных сообщений, которые оно может представлять. Если задана строка s, состоящая из цифр и символов '*', верните количество способов ее декодирования. Поскольку ответ может быть очень большим, верните его по модулю 109 + 7.

Пример:
Input: s = "*"
Output: 9


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

1⃣Инициализация: Создайте массив dp, где dp[i] представляет количество способов декодирования подстроки s[0:i]. Установите начальные значения dp[0] = 1 (пустая строка имеет один способ декодирования).

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

3⃣Модульное вычисление: Поскольку количество способов декодирования может быть большим, вычисляйте результаты по модулю 10^9 + 7.

😎 Решение:
class Solution {
func numDecodings(_ s: String) -> Int {
let MOD = 1_000_000_007
let n = s.count
var dp = [Int](repeating: 0, count: n + 1)
dp[0] = 1
let sArray = Array(s)

for i in 1...n {
if sArray[i - 1] == "*" {
dp[i] = 9 * dp[i - 1]
} else if sArray[i - 1] != "0" {
dp[i] = dp[i - 1]
}

if i > 1 {
if sArray[i - 2] == "*" {
if sArray[i - 1] == "*" {
dp[i] += 15 * dp[i - 2]
} else if sArray[i - 1] <= "6" {
dp[i] += 2 * dp[i - 2]
} else {
dp[i] += dp[i - 2]
}
} else if sArray[i - 2] == "1" {
if sArray[i - 1] == "*" {
dp[i] += 9 * dp[i - 2]
} else {
dp[i] += dp[i - 2]
}
} else if sArray[i - 2] == "2" {
if sArray[i - 1] == "*" {
dp[i] += 6 * dp[i - 2]
} else if sArray[i - 1] <= "6" {
dp[i] += dp[i - 2]
}
}
}

dp[i] %= MOD
}

return dp[n]
}
}


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

Решите заданное уравнение и верните значение 'x' в виде строки "x=#value". Уравнение содержит только операции '+', '-', переменную 'x' и ее коэффициент. Вы должны вернуть "No solution", если для уравнения нет решения, или "Infinite solutions", если для уравнения существует бесконечное количество решений. Если для уравнения существует ровно одно решение, мы убеждаемся, что значение 'x' является целым числом.

Пример:
Input: s = "*"
Output: 9


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

1⃣Разделение уравнения: Разделите уравнение на левую и правую части относительно знака равенства '='.

2⃣Парсинг и упрощение: Пройдитесь по каждой части уравнения, упрощая ее до суммы коэффициентов 'x' и числовых значений.

3⃣Решение уравнения: Используйте уравнение вида ax + b = cx + d, чтобы решить для 'x'. Если коэффициенты 'x' равны и числовые значения равны, уравнение имеет бесконечное количество решений. Если коэффициенты 'x' равны, но числовые значения различны, решения нет. В противном случае вычислите значение 'x'.

😎 Решение:
class Solution {
func solveEquation(_ equation: String) -> String {
func parse(_ s: String) -> (Int, Int) {
var coeff = 0
var constPart = 0
var sign = 1
var num = 0
var i = s.startIndex

while i < s.endIndex {
if s[i] == "+" {
sign = 1
i = s.index(after: i)
} else if s[i] == "-" {
sign = -1
i = s.index(after: i)
} else if s[i].isNumber {
num = 0
while i < s.endIndex && s[i].isNumber {
num = num * 10 + s[i].wholeNumberValue!
i = s.index(after: i)
}
if i < s.endIndex && s[i] == "x" {
coeff += sign * num
i = s.index(after: i)
} else {
constPart += sign * num
}
} else if s[i] == "x" {
coeff += sign
i = s.index(after: i)
}
}
return (coeff, constPart)
}

let parts = equation.split(separator: "=")
let (leftCoeff, leftConst) = parse(String(parts[0]))
let (rightCoeff, rightConst) = parse(String(parts[1]))

let coeff = leftCoeff - rightCoeff
let constPart = rightConst - leftConst

if coeff == 0 {
return constPart == 0 ? "Infinite solutions" : "No solution"
}
return "x=\(constPart / coeff)"
}
}


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

В MATLAB есть удобная функция под названием reshape, которая может преобразовать матрицу размером m x n в новую матрицу с другим размером r x c, сохраняя исходные данные.

Вам дана матрица m x n mat и два целых числа r и c, представляющие количество строк и столбцов желаемой преобразованной матрицы.

Преобразованная матрица должна быть заполнена всеми элементами исходной матрицы в том же порядке обхода строк, в котором они были.

Если операция преобразования с заданными параметрами возможна и допустима, выведите новую преобразованную матрицу; в противном случае выведите исходную матрицу.

Пример:
Input: mat = [[1,2],[3,4]], r = 1, c = 4
Output: [[1,2,3,4]]


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

1⃣Проверить, можно ли преобразовать матрицу с заданными параметрами r и c. Это возможно, если произведение m * n равно произведению r * c. Если преобразование невозможно, вернуть исходную матрицу.

2⃣Создать новый массив для хранения преобразованной матрицы. Перебрать все элементы исходной матрицы и вставить их в новый массив в порядке обхода строк.

3⃣Вернуть преобразованную матрицу, если преобразование возможно, иначе вернуть исходную матрицу.

😎 Решение:
class Solution {
func matrixReshape(_ mat: [[Int]], _ r: Int, _ c: Int) -> [[Int]] {
let m = mat.count
let n = mat[0].count
if m * n != r * c {
return mat
}
var reshapedMatrix = Array(repeating: Array(repeating: 0, count: c), count: r)
var row = 0
var col = 0
for i in 0..<m {
for j in 0..<n {
reshapedMatrix[row][col] = mat[i][j]
col += 1
if col == c {
col = 0
row += 1
}
}
}
return reshapedMatrix
}
}


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

Даны две строки s1 и s2. Верните true, если s2 содержит перестановку s1, или false в противном случае.

Другими словами, верните true, если одна из перестановок s1 является подстрокой s2.

Пример:
Input: s1 = "ab", s2 = "eidbaooo"
Output: true
Explanation: s2 contains one permutation of s1 ("ba").


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

1⃣Создать массив для подсчета символов в строке s1. Затем создать аналогичный массив для первых len(s1) символов строки s2.
2⃣Использовать скользящее окно для перемещения по строке s2. Для каждой позиции окна обновлять массив подсчета символов и сравнивать его с массивом для строки s1.
3⃣Если массивы совпадают на любом этапе, вернуть true. Если окно достигает конца строки s2 и совпадений не найдено, вернуть false.

😎 Решение:
class Solution {
func checkInclusion(_ s1: String, _ s2: String) -> Bool {
let s1Len = s1.count, s2Len = s2.count
if s1Len > s2Len { return false }

let s1Arr = Array(s1), s2Arr = Array(s2)
var s1Count = [Int](repeating: 0, count: 26)
var s2Count = [Int](repeating: 0, count: 26)

for i in 0..<s1Len {
s1Count[Int(s1Arr[i].asciiValue! - Character("a").asciiValue!)] += 1
s2Count[Int(s2Arr[i].asciiValue! - Character("a").asciiValue!)] += 1
}

for i in 0..<(s2Len - s1Len) {
if s1Count == s2Count { return true }
s2Count[Int(s2Arr[i].asciiValue! - Character("a").asciiValue!)] -= 1
s2Count[Int(s2Arr[i + s1Len].asciiValue! - Character("a").asciiValue!)] += 1
}

return s1Count == s2Count
}
}


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

Разработайте свою реализацию круговой двусторонней очереди (deque). Реализуйте класс MyCircularDeque: MyCircularDeque(int k) Инициализирует deque с максимальным размером k. boolean insertFront() Добавляет элемент в переднюю часть Deque. Возвращает true, если операция прошла успешно, или false в противном случае. boolean insertLast() Добавляет элемент в заднюю часть Deque. Возвращает true, если операция выполнена успешно, или false в противном случае. boolean deleteFront() Удаляет элемент из передней части Deque. Возвращает true, если операция прошла успешно, или false в противном случае. boolean deleteLast() Удаляет элемент из задней части Deque. Возвращает true, если операция прошла успешно, или false в противном случае. int getFront() Возвращает передний элемент из Deque. Возвращает -1, если Deque пуст. int getRear() Возвращает последний элемент из Deque. Возвращает -1, если Deque пуст. boolean isEmpty() Возвращает true, если Deque пуст, или false в противном случае. boolean isFull() Возвращает true, если Deque полон, или false в противном случае.

Пример:
Input
["MyCircularDeque", "insertLast", "insertLast", "insertFront", "insertFront", "getRear", "isFull", "deleteLast", "insertFront", "getFront"]
[[3], [1], [2], [3], [4], [], [], [], [4], []]
Output
[null, true, true, true, false, 2, true, true, true, 4]


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

1⃣Инициализация и проверка состояний: Реализуйте конструктор для инициализации кольцевой двусторонней очереди заданного размера и методы для проверки пустоты и полноты очереди.
2⃣Операции вставки: Реализуйте методы вставки элементов в переднюю и заднюю части очереди с учетом кольцевой структуры.
3⃣Операции удаления: Реализуйте методы удаления элементов из передней и задней частей очереди с учетом кольцевой структуры и методы для получения переднего и заднего элементов очереди.

😎 Решение:
class MyCircularDeque {
private var deque: [Int]
private var front: Int
private var rear: Int
private var size: Int
private var capacity: Int

init(_ k: Int) {
self.capacity = k
self.deque = [Int](repeating: 0, count: k)
self.front = 0
self.rear = 0
self.size = 0
}

func insertFront(_ value: Int) -> Bool {
if isFull() {
return false
}
front = (front - 1 + capacity) % capacity
deque[front] = value
size += 1
return true
}

func insertLast(_ value: Int) -> Bool {
if isFull() {
return false
}
deque[rear] = value
rear = (rear + 1) % capacity
size += 1
return true
}

func deleteFront() -> Bool {
if isEmpty() {
return false
}
front = (front + 1) % capacity
size -= 1
return true
}

func deleteLast() -> Bool {
if isEmpty() {
return false
}
rear = (rear - 1 + capacity) % capacity
size -= 1
return true
}

func getFront() -> Int {
if isEmpty() {
return -1
}
return deque[front]
}

func getRear() -> Int {
if isEmpty() {
return -1
}
return deque[(rear - 1 + capacity) % capacity]
}

func isEmpty() -> Bool {
return size == 0
}

func isFull() -> Bool {
return size == capacity
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
#hard
Задача: 568. Maximum Vacation Days

LeetCode хочет предоставить одному из своих лучших сотрудников возможность путешествовать по n городам для сбора задач по алгоритмам. Однако, как говорится, "делу время, потехе час". Вы можете брать отпуска в некоторых конкретных городах и неделях. Ваша задача — спланировать поездку, чтобы максимально увеличить количество дней отпуска, которые вы сможете взять, соблюдая при этом определенные правила и ограничения.

Правила и ограничения:
Вы можете путешествовать только между n городами, обозначенными индексами от 0 до n-1. Изначально вы находитесь в городе с индексом 0 в понедельник.
Города связаны рейсами. Рейсы представлены матрицей n x n, называемой flights, представляющей статус авиалинии от города i до города j. Если рейса из города i в город j нет, flights[i][j] == 0; иначе flights[i][j] == 1. Также для всех i выполняется flights[i][i] == 0.
У вас есть k недель (каждая неделя состоит из семи дней) для путешествий. Вы можете летать не более одного раза в день и можете летать только утром каждого понедельника. Время полета настолько короткое, что его влияние не учитывается.
Для каждого города у вас есть ограниченные дни отпуска в разные недели, заданные матрицей n x k, называемой days. Значение days[i][j] представляет максимальное количество дней отпуска, которые вы можете взять в городе i на неделе j.
Вы можете оставаться в городе большее количество дней, чем количество дней отпуска, но вы должны работать в дополнительные дни, которые не будут учитываться как дни отпуска.
Даны две матрицы flights и days, верните максимальное количество дней отпуска, которые вы можете взять в течение k недель.

Пример:
Input: flights = [[0,1,1],[1,0,1],[1,1,0]], days = [[1,3,1],[6,0,3],[3,3,3]]
Output: 12
Explanation:
One of the best strategies is:
1st week : fly from city 0 to city 1 on Monday, and play 6 days and work 1 day.
(Although you start at city 0, we could also fly to and start at other cities since it is Monday.)
2nd week : fly from city 1 to city 2 on Monday, and play 3 days and work 4 days.
3rd week : stay at city 2, and play 3 days and work 4 days.
Ans = 6 + 3 + 3 = 12.


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

1⃣Использовать функцию dfs (поиск в глубину), которая возвращает количество отпускных дней, которые можно взять, начиная с текущего города cur_city и текущей недели weekno. В каждом вызове функции проходить по всем городам и находить все города, которые связаны с текущим городом. Такой город обозначен 1 в соответствующей позиции flights[cur_city][i].

2⃣Для текущего города можно либо остаться в нем, либо поехать в связанный город. Обозначим город, в который меняется расположение, как j. После смены города нужно найти количество отпускных дней, которые можно взять, начиная с нового города и с новой недели. Это количество отпускных дней можно представить как: days[j][weekno] + dfs(flights, days, j, weekno + 1).

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

😎 Решение:
class Solution {
func maxVacationDays(_ flights: [[Int]], _ days: [[Int]]) -> Int {
let n = flights.count
let k = days[0].count
var memo = Array(repeating: Array(repeating: -1, count: k), count: n)

func dfs(_ cur_city: Int, _ weekno: Int) -> Int {
if weekno == k {
return 0
}
if memo[cur_city][weekno] != -1 {
return memo[cur_city][weekno]
}
var max_vac = 0
for next_city in 0..<n {
if cur_city == next_city || flights[cur_city][next_city] == 1 {
max_vac = max(max_vac, days[next_city][weekno] + dfs(next_city, weekno + 1))
}
}
memo[cur_city][weekno] = max_vac
return max_vac
}

return dfs(0, 0)
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
#hard
Задача: 642. Design Search Autocomplete System

Разработайте свою реализацию круговой двусторонней очереди (deque). Реализуйте класс MyCircularDeque: MyCircularDeque(int k) Инициализирует deque с максимальным размером k. boolean insertFront() Добавляет элемент в переднюю часть Deque. Возвращает true, если операция прошла успешно, или false в противном случае. boolean insertLast() Добавляет элемент в заднюю часть Deque. Возвращает true, если операция выполнена успешно, или false в противном случае. boolean deleteFront() Удаляет элемент из передней части Deque. Возвращает true, если операция прошла успешно, или false в противном случае. boolean deleteLast() Удаляет элемент из задней части Deque. Возвращает true, если операция прошла успешно, или false в противном случае. int getFront() Возвращает передний элемент из Deque. Возвращает -1, если Deque пуст. int getRear() Возвращает последний элемент из Deque. Возвращает -1, если Deque пуст. boolean isEmpty() Возвращает true, если Deque пуст, или false в противном случае. boolean isFull() Возвращает true, если Deque полон, или false в противном случае.

Пример:
Input
["MyCircularDeque", "insertLast", "insertLast", "insertFront", "insertFront", "getRear", "isFull", "deleteLast", "insertFront", "getFront"]
[[3], [1], [2], [3], [4], [], [], [], [4], []]
Output
[null, true, true, true, false, 2, true, true, true, 4]


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

1⃣Инициализация и проверка состояний: Реализуйте конструктор для инициализации кольцевой двусторонней очереди заданного размера и методы для проверки пустоты и полноты очереди.
2⃣Операции вставки: Реализуйте методы вставки элементов в переднюю и заднюю части очереди с учетом кольцевой структуры.
3⃣Операции удаления: Реализуйте методы удаления элементов из передней и задней частей очереди с учетом кольцевой структуры и методы для получения переднего и заднего элементов очереди.

😎 Решение:
class TrieNode {
var children = [Character: TrieNode]()
var count = [String: Int]()
}

class AutocompleteSystem {
private var root = TrieNode()
private var prefix = ""

init(_ sentences: [String], _ times: [Int]) {
for i in 0..<sentences.count {
add(sentences[i], times[i])
}
}

private func add(_ sentence: String, _ count: Int) {
var node = root
for char in sentence {
if node.children[char] == nil {
node.children[char] = TrieNode()
}
node = node.children[char]!
node.count[sentence, default: 0] += count
}
}

func input(_ c: Character) -> [String] {
if c == "#" {
add(prefix, 1)
prefix = ""
return []
}

prefix.append(c)
var node = root
for char in prefix {
if node.children[char] == nil {
return []
}
node = node.children[char]!
}

let pq = node.count.sorted { lhs, rhs in
if lhs.value == rhs.value {
return lhs.key < rhs.key
}
return lhs.value > rhs.value
}

return Array(pq.prefix(3)).map { $0.key }
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
#easy
Задача: 643. Maximum Average Subarray I

Вам дан целочисленный массив nums, состоящий из n элементов, и целое число k. Найдите смежный подмассив, длина которого равна k и который имеет максимальное среднее значение, и верните это значение. Принимается любой ответ с погрешностью вычислений менее 10-5.

Пример:
Input: nums = [1,12,-5,-6,50,3], k = 4
Output: 12.75000


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

1⃣Инициализация скользящего окна: Вычислите сумму первых k элементов массива nums. Это будет начальное значение максимальной суммы.
2⃣Перемещение окна: Перемещайте окно длиной k по массиву, добавляя следующий элемент и убирая предыдущий, чтобы поддерживать сумму текущего окна.
3⃣Обновление максимальной суммы: На каждом шаге обновляйте максимальную сумму, если текущая сумма больше, и в конце верните среднее значение этой суммы.

😎 Решение:
class Solution {
func findMaxAverage(_ nums: [Int], _ k: Int) -> Double {
var currentSum = nums.prefix(k).reduce(0, +)
var maxSum = currentSum

for i in k..<nums.count {
currentSum += nums[i] - nums[i - k]
maxSum = max(maxSum, currentSum)
}

return Double(maxSum) / Double(k)
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
#hard
Задача: 644. Maximum Average Subarray II

Вам дан целочисленный массив nums, состоящий из n элементов, и целое число k. Найдите смежный подмассив, длина которого больше или равна k и который имеет максимальное среднее значение, и верните это значение. Принимается любой ответ с погрешностью вычислений менее 10-5.

Пример:
Input: nums = [1,12,-5,-6,50,3], k = 4
Output: 12.75000


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

1⃣Используйте скользящее окно длины k для нахождения начального среднего значения.
2⃣Перемещайте окно по массиву, добавляя следующий элемент и убирая предыдущий, обновляя текущее среднее значение.
3⃣Следите за максимальным средним значением и верните его после проверки всех возможных окон.

😎 Решение:
func findMaxAverage(_ nums: [Int], _ k: Int) -> Double {
var currSum = nums[0..<k].reduce(0, +)
var maxSum = currSum
for i in k..<nums.count {
currSum += nums[i] - nums[i - k]
if currSum > maxSum {
maxSum = currSum
}
}
return Double(maxSum) / Double(k)
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
#easy
Задача: 645. Set Mismatch

У вас есть набор целых чисел s, который изначально содержит все числа от 1 до n. К сожалению, из-за какой-то ошибки одно из чисел в s продублировалось в другое число в наборе, что привело к повторению одного числа и потере другого. Вам дан целочисленный массив nums, представляющий состояние данных в этом наборе после ошибки. Найдите число, которое встречается дважды, и число, которое отсутствует, и верните их в виде массива.

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


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

1⃣Пройдите по массиву, используя набор для отслеживания чисел, чтобы определить дублированное число.
2⃣Определите отсутствующее число, используя сумму чисел от 1 до n и текущую сумму массива.
3⃣Верните дублированное и отсутствующее числа в виде массива.

😎 Решение:
func findErrorNums(_ nums: [Int]) -> [Int] {
var numSet = Set<Int>()
var duplicate = -1
let n = nums.count
for num in nums {
if numSet.contains(num) {
duplicate = num
}
numSet.insert(num)
}
let missing = (n * (n + 1)) / 2 - numSet.reduce(0, +)
return [duplicate, missing]
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
#hard
Задача: 656. Coin Path

Вам дан целочисленный массив монет (1-индексированный) длины n и целое число maxJump. Вы можете перейти на любой индекс i массива coins, если coins[i] != -1 и вы должны заплатить coins[i] при посещении индекса i. Кроме того, если вы в данный момент находитесь на индексе i, вы можете перейти только на любой индекс i + k, где i + k <= n и k - значение в диапазоне [1, maxJump]. Изначально вы находитесь на индексе 1 (coins[1] не -1). Вы хотите найти путь, который достигнет индекса n с минимальной стоимостью. Верните целочисленный массив индексов, которые вы посетите в таком порядке, чтобы достичь индекса n с минимальной стоимостью. Если существует несколько путей с одинаковой стоимостью, верните лексикографически наименьший такой путь. Если невозможно достичь индекса n, возвращается пустой массив. Путь p1 = [Pa1, Pa2, ..., Pax] длины x лексикографически меньше, чем p2 = [Pb1, Pb2, ..., Pbx] длины y, если и только если при первом j, где Paj и Pbj отличаются, Paj < Pbj; если такого j нет, то x < y.

Пример:
Input: coins = [1,2,4,-1,2], maxJump = 2
Output: [1,3,5]


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

1⃣Используйте динамическое программирование для нахождения минимальной стоимости до каждого индекса, начиная с первого.
2⃣Храните путь до каждого индекса для отслеживания наименьшего лексикографического пути.
3⃣Используя полученную информацию, восстановите путь с минимальной стоимостью до последнего индекса.

😎 Решение:
import Foundation

func minCostPath(_ coins: [Int], _ maxJump: Int) -> [Int] {
let n = coins.count
if coins[0] == -1 { return [] }

var dp = [Int](repeating: Int.max, count: n)
dp[0] = coins[0]
var path = Array(repeating: [Int](), count: n)
path[0] = [1]

var heap = [(cost: Int, index: Int)]()
heap.append((coins[0], 0))

while !heap.isEmpty {
heap.sort(by: { $0.cost < $1.cost })
let (current_cost, i) = heap.removeFirst()
if current_cost > dp[i] { continue }
for k in 1...maxJump {
if i + k < n && coins[i + k] != -1 {
let new_cost = current_cost + coins[i + k]
if new_cost < dp[i + k] || (new_cost == dp[i + k] && path[i] + [i + k + 1] < path[i + k]) {
dp[i + k] = new_cost
path[i + k] = path[i] + [i + k + 1]
heap.append((new_cost, i + k))
}
}
}
}

return dp[n - 1] == Int.max ? [] : path[n - 1]
}


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

Учитывая корень двоичного дерева, постройте строковую матрицу res с индексом 0 размером m x n, которая представляет собой форматированную раскладку дерева. Форматированная матрица должна быть построена по следующим правилам: высота дерева равна height, количество строк m должно быть равно height + 1. Количество столбцов n должно быть равно 2height+1 - 1. Поместите корневой узел в середину верхней строки (более формально, в позицию res[0][(n-1)/2]).
Для каждого узла, который был помещен в матрицу в позицию res[r][c], поместите его левого ребенка в res[r+1][c-2height-r-1], а правого - в res[r+1][c+2height-r-1]. Продолжайте этот процесс, пока не будут размещены все узлы дерева. Любые пустые ячейки должны содержать пустую строку "". Верните построенную матрицу res.

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


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

1⃣Найдите высоту дерева и определите размер матрицы (m x n).

2⃣Рекурсивно разместите узлы в матрице, начиная с корневого узла.

3⃣Верните заполненную матрицу.

😎 Решение:
class TreeNode {
var val: Int
var left: TreeNode?
var right: TreeNode?
init(_ val: Int) {
self.val = val
self.left = nil
self.right = nil
}
}

func findHeight(_ root: TreeNode?) -> Int {
guard let root = root else { return -1 }
return 1 + max(findHeight(root.left), findHeight(root.right))
}

func fill(_ res: inout [[String]], _ root: TreeNode?, _ r: Int, _ c: Int, _ height: Int) {
guard let root = root else { return }
res[r][c] = String(root.val)
if let left = root.left {
fill(&res, left, r + 1, c - (1 << (height - r - 1)), height)
}
if let right = root.right {
fill(&res, right, r + 1, c + (1 << (height - r - 1)), height)
}
}

func printTree(_ root: TreeNode?) -> [[String]] {
let height = findHeight(root)
let m = height + 1
let n = (1 << (height + 1)) - 1
var res = Array(repeating: Array(repeating: "", count: n), count: m)
fill(&res, root, 0, (n - 1) / 2, height)
return res
}


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

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

Пример:
Input: nums = [3,2,1,6,0,5]
Output: [6,3,5,null,2,0,null,null,1]


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

1⃣Найдите максимальное значение в текущем подмассиве и создайте узел с этим значением.

2⃣Рекурсивно постройте левое поддерево для подмассива слева от максимального значения.

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

😎 Решение:
public class TreeNode {
public var val: Int
public var left: TreeNode?
public var right: TreeNode?
public init(_ val: Int) {
self.val = val
self.left = nil
self.right = nil
}
}

func constructMaximumBinaryTree(_ nums: [Int]) -> TreeNode? {
guard !nums.isEmpty else { return nil }
let maxIndex = nums.firstIndex(of: nums.max()!)!
let root = TreeNode(nums[maxIndex])
root.left = constructMaximumBinaryTree(Array(nums[..<maxIndex]))
root.right = constructMaximumBinaryTree(Array(nums[(maxIndex + 1)...]))
return root
}


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