TGViewer
Channel Public Channel
Swift | LeetCode

Swift | LeetCode

@easy_swift_task

Сайт: https://easyoffer.ru/
Все каналы: t.me/+xGeAw6ckJ4liYzQy

Контакт для рекламы: @sendme_ads
Subscribers
1.3K
Photos
156
Videos
0
Links
1.5K

Showing posts older than #1394 · Back to latest

Older Posts 20 shown
Post #1393 89
Задача: 315. Count of Smaller Numbers After Self
Сложность: hard

Дан целочисленный массив nums, верните целочисленный массив counts, где counts[i] - это количество элементов справа от nums[i], которые меньше nums[i].

Пример:
Input: nums = [5,2,6,1]
Output: [2,1,1,0]
Explanation:
To the right of 5 there are 2 smaller elements (2 and 1).
To the right of 2 there is only 1 smaller element (1).
To the right of 6 there is 1 smaller element (1).
To the right of 1 there is 0 smaller element.


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

1⃣Реализуйте дерево отрезков (segment tree). Поскольку дерево инициализируется нулями, нужно реализовать только операции обновления и запроса. Установите смещение offset = 10^4.

2⃣Итерация по каждому числу в nums в обратном порядке. Для каждого числа выполните следующие действия:
Смещайте число на num + offset.
Запросите количество элементов в дереве отрезков, которые меньше текущего числа.
Обновите счетчик текущего числа в дереве отрезков.

3⃣Верните результат.

😎 Решение:
class Solution {
func countSmaller(_ nums: [Int]) -> [Int] {
let offset = 10000
let size = 2 * 10000 + 1
var tree = [Int](repeating: 0, count: size * 2)
var result = [Int]()

for num in nums.reversed() {
let smallerCount = query(0, num + offset, tree, size)
result.append(smallerCount)
update(num + offset, 1, tree, size)
}

return result.reversed()
}

private func update(_ index: Int, _ value: Int, _ tree: inout [Int], _ size: Int) {
var idx = index + size
tree[idx] += value
while idx > 1 {
idx /= 2
tree[idx] = tree[idx * 2] + tree[idx * 2 + 1]
}
}

private func query(_ left: Int, _ right: Int, _ tree: [Int], _ size: Int) -> Int {
var result = 0
var l = left + size
var r = right + size
while l < r {
if l % 2 == 1 {
result += tree[l]
l += 1
}
l /= 2
if r % 2 == 1 {
r -= 1
result += tree[r]
}
r /= 2
}
return result
}
}


Ставь 👍 и забирай 📚 Базу знаний
Post #1392 97
Задача: 634. Find the Derangement of An Array
Сложность: medium

В комбинаторной математике отклонение - это перестановка элементов множества таким образом, что ни один элемент не оказывается на прежнем месте. Вам дано целое число 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]
}


Ставь 👍 и забирай 📚 Базу знаний
Post #1391 98
Задача: 201. Bitwise AND of Numbers Range
Сложность: medium

Даны два целых числа left и right, которые представляют диапазон [left, right], верните побитовое И всех чисел в этом диапазоне включительно.

Пример:
Input: left = 5, right = 7
Output: 4


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

1⃣Сдвигать left и right вправо, пока они не станут равными.

2⃣Подсчитать количество сдвигов.

3⃣Сдвинуть left влево на количество сдвигов и вернуть результат.

😎 Решение:
class Solution {
func rangeBitwiseAnd(_ left: Int, _ right: Int) -> Int {
var left = left
var right = right
var shift = 0

while left < right {
left >>= 1
right >>= 1
shift += 1
}

return left << shift
}
}


Ставь 👍 и забирай 📚 Базу знаний
Post #1390 112
Задача: 1137. N-th Tribonacci Number
Сложность: easy

Трибоначчи последовательность Tn определяется следующим образом:

T0 = 0, T1 = 1, T2 = 1, и Tn+3 = Tn + Tn+1 + Tn+2 для n >= 0.

Дано n, вернуть значение Tn.

Пример:
Input: n = 4
Output: 4
Explanation:
T_3 = 0 + 1 + 1 = 2
T_4 = 1 + 1 + 2 = 4


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

1⃣Если n < 3, вернуть значение n-го терма, как указано в описании задачи.

2⃣Инициализировать a, b и c как базовые случаи. Установить a = 0, b = 1, c = 1.

3⃣Для следующих n - 2 шагов обновлять a, b, c следующим образом: a = b, b = c, c = a + b + c. Вернуть c.

😎 Решение:
class Solution {
func tribonacci(_ n: Int) -> Int {
if n < 3 {
return n > 0 ? 1 : 0
}

var a = 0, b = 1, c = 1
for _ in 0..<n-2 {
let tmp = a + b + c
a = b
b = c
c = tmp
}

return c
}
}


Ставь 👍 и забирай 📚 Базу знаний
  • 💊 1
Post #1389 104
Задача: 491. Non-decreasing Subsequences
Сложность: medium

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

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


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

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

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

3⃣Возврат результата
После завершения всех рекурсивных вызовов преобразуйте множество результатов в список и верните его.

😎 Решение:
class Solution {
func findSubsequences(_ nums: [Int]) -> [[Int]] {
var result = Set<[Int]>()
var sequence = [Int]()
backtrack(nums, 0, &sequence, &result)
return Array(result)
}

func backtrack(_ nums: [Int], _ index: Int, _ sequence: inout [Int], _ result: inout Set<[Int]>) {
if index == nums.count {
if sequence.count >= 2 {
result.insert(sequence)
}
return
}
if sequence.isEmpty || sequence.last! <= nums[index] {
sequence.append(nums[index])
backtrack(nums, index + 1, &sequence, &result)
sequence.removeLast()
}
backtrack(nums, index + 1, &sequence, &result)
}
}


Ставь 👍 и забирай 📚 Базу знаний
Post #1388 92
Задача: 815. Bus Routes
Сложность: hard

Дан массив routes, представляющий автобусные маршруты, где routes[i] - это автобусный маршрут, который i-й автобус повторяет бесконечно.

Например, если routes[0] = [1, 5, 7], это означает, что 0-й автобус путешествует в последовательности 1 -> 5 -> 7 -> 1 -> 5 -> 7 -> 1 -> ... бесконечно.
Вы начинаете на автобусной остановке source (вы изначально не находитесь в автобусе) и хотите добраться до автобусной остановки target. Перемещаться между автобусными остановками можно только на автобусах.

Верните наименьшее количество автобусов, которые вам нужно взять, чтобы доехать от source до target. Верните -1, если это невозможно.

Пример:
Input: routes = [[1,2,7],[3,6,7]], source = 1, target = 6
Output: 2
Explanation: The best strategy is take the first bus to the bus stop 7, then take the second bus to the bus stop 6.


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

1⃣Верните 0, если source и target совпадают. Инициализируйте пустую карту adjList, чтобы хранить ребра, где ключ - это автобусная остановка, а значение - список целых чисел, обозначающих индексы маршрутов, которые имеют эту остановку. Инициализируйте пустую очередь q и неупорядоченное множество vis, чтобы отслеживать посещенные маршруты. Вставьте начальные маршруты в очередь q и отметьте их посещенными в vis.

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

3⃣Верните -1 после завершения обхода в ширину (BFS).

😎 Решение:
class Solution {
func numBusesToDestination(_ routes: [[Int]], _ source: Int, _ target: Int) -> Int {
if source == target { return 0 }

var adjList = [Int: [Int]]()
for route in 0..<routes.count {
for stop in routes[route] {
adjList[stop, default: []].append(route)
}
}

var q = [Int]()
var vis = Set<Int>()
for route in adjList[source] ?? [] {
q.append(route)
vis.insert(route)
}

var busCount = 1
while !q.isEmpty {
for _ in 0..<q.count {
let route = q.removeFirst()
for stop in routes[route] {
if stop == target { return busCount }
for nextRoute in adjList[stop] ?? [] {
if !vis.contains(nextRoute) {
vis.insert(nextRoute)
q.append(nextRoute)
}
}
}
}
busCount += 1
}
return -1
}
}


Ставь 👍 и забирай 📚 Базу знаний
Post #1387 100
Задача: 1422. Maximum Score After Splitting a String
Сложность: easy

Дана строка s из нулей и единиц. Верните максимальное количество очков после разбиения строки на две непустые подстроки (т.е. левую подстроку и правую подстроку).

Количество очков после разбиения строки - это количество нулей в левой подстроке плюс количество единиц в правой подстроке.

Пример:
Input: s = "011101"
Output: 5
Explanation:
All possible ways of splitting s into two non-empty substrings are:
left = "0" and right = "11101", score = 1 + 4 = 5
left = "01" and right = "1101", score = 1 + 3 = 4
left = "011" and right = "101", score = 1 + 2 = 3
left = "0111" and right = "01", score = 1 + 1 = 2
left = "01110" and right = "1", score = 2 + 1 = 3


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

1⃣Посчитайте количество единиц в строке и инициализируйте счётчики нулей и максимального значения.

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

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

😎 Решение:
class Solution {
func maxScore(_ s: String) -> Int {
let ones = s.filter { $0 == "1" }.count
var zeros = 0, ans = 0, countOnes = ones

for i in 0..<s.count - 1 {
let char = s[s.index(s.startIndex, offsetBy: i)]
if char == "1" {
countOnes -= 1
} else {
zeros += 1
}
ans = max(ans, zeros + countOnes)
}
return ans
}
}


Ставь 👍 и забирай 📚 Базу знаний
  • 💊 1
Post #1386 103
Задача: 412. Fizz Buzz
Сложность: easy

Учитывая целое число n, верните строковый массив answer (с индексом 1), где: answer[i] == "FizzBuzz", если i делится на 3 и 5. answer[i] == "Fizz", если i делится на 3. answer[i] == "Buzz", если i делится на 5. answer[i] == i (как строка), если ни одно из перечисленных условий не верно.

Пример:
Input: n = 3
Output: ["1","2","Fizz"]


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

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

2⃣Пройдите по всем числам от 1 до n и для каждого числа выполните проверку: Если число делится на 3 и на 5, добавьте "FizzBuzz". Если число делится на 3, добавьте "Fizz". Если число делится на 5, добавьте "Buzz". Если ни одно из условий не выполнено, добавьте само число как строку.

3⃣Верните полученный список.

😎 Решение:
func fizzBuzz(_ n: Int) -> [String] {
var answer = [String]()
for i in 1...n {
if i % 3 == 0 && i % 5 == 0 {
answer.append("FizzBuzz")
} else if i % 3 == 0 {
answer.append("Fizz")
} else if i % 5 == 0 {
answer.append("Buzz")
} else {
answer.append(String(i))
}
}
return answer
}


Ставь 👍 и забирай 📚 Базу знаний
Post #1385 102
Задача: 1243. Array Transformation
Сложность: easy

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

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


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

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

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

3⃣Первый и последний элементы массива остаются неизменными.

😎 Решение:
class Solution {
func transformArray(_ arr: [Int]) -> [Int] {
var arr = arr
var changed = false
repeat {
changed = false
var newArr = arr
for i in 1..<arr.count - 1 {
if arr[i] < arr[i - 1] && arr[i] < arr[i + 1] {
newArr[i] += 1
changed = true
} else if arr[i] > arr[i - 1] && arr[i] > arr[i + 1] {
newArr[i] -= 1
changed = true
}
}
arr = newArr
} while changed
return arr
}
}


Ставь 👍 и забирай 📚 Базу знаний
Post #1384 103
Задача: 1278. Palindrome Partitioning III
Сложность: hard

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

Пример:
Input: s = "abc", k = 2
Output: 1


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

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

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

3⃣Верните минимальное количество изменений, найденное во втором шаге.

😎 Решение:
class Solution {
func minChangesToMakePalindrome(_ s: String, _ k: Int) -> Int {
let n = s.count
let sArray = Array(s)

func minChangeToPalindrome(_ i: Int, _ j: Int) -> Int {
var changes = 0
var i = i
var j = j
while i < j {
if sArray[i] != sArray[j] {
changes += 1
}
i += 1
j -= 1
}
return changes
}

var dp1 = Array(repeating: Array(repeating: 0, count: n), count: n)
for length in 1...n {
for i in 0...(n - length) {
let j = i + length - 1
dp1[i][j] = minChangeToPalindrome(i, j)
}
}

var dp2 = Array(repeating: Array(repeating: Int.max, count: k + 1), count: n + 1)
dp2[0][0] = 0

for i in 1...n {
for kk in 1...k {
for j in 0..<i {
dp2[i][kk] = min(dp2[i][kk], dp2[j][kk - 1] + dp1[j][i - 1])
}
}
}

return dp2[n][k]
}
}


Ставь 👍 и забирай 📚 Базу знаний
Post #1383 83
Задача: 1663. Smallest String With A Given Numeric Value
Сложность: medium

Числовое значение строчной буквы определяется ее позицией (начиная с 1) в алфавите, поэтому числовое значение a равно 1, числовое значение b равно 2, числовое значение c равно 3 и так далее.

Числовое значение строки, состоящей из строчных букв, определяется как сумма числовых значений ее символов. Например, числовое значение строки "abe" равно 1 + 2 + 5 = 8.

Вам даны два целых числа n и k. Верните лексикографически наименьшую строку длиной n с числовым значением, равным k.

Обратите внимание, что строка x лексикографически меньше строки y, если x идет перед y в словарном порядке, то есть либо x является префиксом y, либо, если i - первая позиция, где x[i] != y[i], то x[i] идет перед y[i] в алфавитном порядке.

Пример:
Input: n = 3, k = 27
Output: "aay"
Explanation: The numeric value of the string is 1 + 1 + 25 = 27, and it is the smallest string with such a value and length equal to 3.


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

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

2⃣Итерация от позиции 1 до n и заполнение символом каждой позиции:
Найти позиции, которые нужно заполнить, исключая текущую позицию, задаваемую переменной positionsLeft как n - position - 1.
Если значение k больше, чем positionsLeft * 26, зарезервировать числовое значение 26 (символ z) для всех оставшихся позиций positionsLeft. Вычислить значение текущей позиции, заданное переменной add, как k - (positionsLeft * 26). Вычесть рассчитанное значение add из k, чтобы найти оставшееся значение k после заполнения текущей позиции.
Иначе, заполнить текущую позицию символом a, имеющим числовое значение 1. Вычесть 1 из k, чтобы найти оставшееся значение k после заполнения текущей позиции.

3⃣Повторять процесс, пока все позиции не будут заполнены.

😎 Решение:
class Solution {
func getSmallestString(_ n: Int, _ k: Int) -> String {
var result = Array(repeating: "a", count: n)
var k = k

for position in 0..<n {
let positionsLeft = (n - position - 1)
if k > positionsLeft * 26 {
let add = k - (positionsLeft * 26)
result[position] = String(UnicodeScalar(97 + add - 1)!)
k -= add
} else {
k -= 1
}
}

return result.joined()
}
}


Ставь 👍 и забирай 📚 Базу знаний
Post #1382 93
Задача: 27. Remove Element
Сложность: easy

Учитывая целочисленный массив nums и целочисленное значение val, удалите все вхождения val в nums на месте. Порядок элементов может быть изменен. Затем верните количество элементов, которые не равны val.

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


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

1⃣Используем указатель k для отслеживания позиции, куда вставлять элементы, не равные val.

2⃣Проходим по массиву и при нахождении элемента, отличного от val, записываем его на позицию k, затем увеличиваем k.

3⃣В конце k будет содержать количество элементов, которые не равны val.

😎Решение:
func removeElement(_ nums: inout [Int], _ val: Int) -> Int {
var k = 0

for i in 0..<nums.count {
if nums[i] != val {
nums[k] = nums[i]
k += 1
}
}

return k
}


Ставь 👍 и забирай 📚 Базу знаний
Post #1381 97
Задача: 716. Max Stack
Сложность: hard

Разработайте структуру данных max-стека, поддерживающую операции со стеком и поиск максимального элемента стека. Реализуйте класс MaxStack: MaxStack() Инициализирует объект стека. void push(int x) Вставляет элемент x в стек. int pop() Удаляет элемент на вершине стека и возвращает его. int top() Получает элемент на вершине стека без его удаления. int peekMax() Получает максимальный элемент в стеке без его удаления. int popMax() Получает максимальный элемент в стеке и удаляет его. Если максимальных элементов несколько, удалите только самый верхний. Вы должны придумать решение, которое поддерживает O(1) для каждого вызова вершины и O(logn) для каждого другого вызова.

Пример:
Input
["MaxStack", "push", "push", "push", "top", "popMax", "top", "peekMax", "pop", "top"]
[[], [5], [1], [5], [], [], [], [], [], []]
Output
[null, null, null, null, 5, 5, 1, 5, 1, 5]


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

1⃣Инициализируйте MaxStack с двумя стеками: один для хранения всех элементов, другой для отслеживания максимальных элементов.

2⃣Для операции push(x) добавьте элемент в оба стека: в основной стек и, если это необходимо, в стек максимумов. Для операции pop() удалите элемент из основного стека и, если этот элемент является текущим максимальным, удалите его и из стека максимумов. Для операции top() верните верхний элемент основного стека.

3⃣Для операции peekMax() верните верхний элемент стека максимумов. Для операции popMax() удалите и верните верхний элемент стека максимумов. Для этого временно извлеките элементы из основного стека до тех пор, пока не будет найден максимальный элемент, затем верните остальные элементы обратно.

😎 Решение:
class MaxStack {

private var stack: [Int]
private var maxStack: [Int]

init() {
stack = []
maxStack = []
}

func push(_ x: Int) {
stack.append(x)
if maxStack.isEmpty || x >= maxStack.last! {
maxStack.append(x)
}
}

func pop() -> Int {
let x = stack.removeLast()
if x == maxStack.last! {
maxStack.removeLast()
}
return x
}

func top() -> Int {
return stack.last!
}

func peekMax() -> Int {
return maxStack.last!
}

func popMax() -> Int {
let maxVal = maxStack.removeLast()
var buffer: [Int] = []
while stack.last! != maxVal {
buffer.append(stack.removeLast())
}
stack.removeLast()
while !buffer.isEmpty {
push(buffer.removeLast())
}
return maxVal
}
}


Ставь 👍 и забирай 📚 Базу знаний
Post #1380 83
Задача: 1319. Number of Operations to Make Network Connected
Сложность: medium

Дано n компьютеров, пронумерованных от 0 до n - 1, соединённых Ethernet-кабелями connections, образующими сеть, где connections[i] = [ai, bi] представляет собой соединение между компьютерами ai и bi. Любой компьютер может достичь любого другого компьютера напрямую или косвенно через сеть.

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

Верните минимальное количество раз, которое необходимо сделать это, чтобы соединить все компьютеры. Если это невозможно, верните -1.

Пример:
Input: n = 4, connections = [[0,1],[0,2],[1,2]]
Output: 1
Explanation: Remove cable between computer 1 and 2 and place between computers 1 and 3.


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

1⃣Проверьте размер connections. Если он меньше n - 1, у нас недостаточно ребер, чтобы соединить весь граф. В этом случае возвращаем -1.

2⃣Создайте список смежности с помощью connections, где adj[x] содержит всех соседей узла x. Создайте целое число numberOfConnectedComponents, которое хранит количество компонент связности в графе. Инициализируйте его значением 0.

3⃣Создайте массив visit длиной n для отслеживания посещенных узлов. Пройдите по всем узлам, и для каждого узла i проверьте, был ли он посещен. Если узел i не был посещен, увеличьте numberOfConnectedComponents на 1 и начните обход DFS:
Используйте функцию dfs для выполнения обхода. Для каждого вызова передавайте узел, ребра и visit в качестве параметров, начиная с узла i.
Отметьте узел как посещенный.
Пройдитесь по всем соседям узла. Если какой-либо сосед еще не был посещен, рекурсивно вызовите dfs с этим соседом в качестве узла.
Верните numberOfConnectedComponents - 1.

😎 Решение
class Solution {
func dfs(_ node: Int, _ adj: [Int: [Int]], _ visit: inout [Bool]) {
visit[node] = true
guard let neighbors = adj[node] else { return }
for neighbor in neighbors {
if !visit[neighbor] {
visit[neighbor] = true
dfs(neighbor, adj, &visit)
}
}
}

func makeConnected(_ n: Int, _ connections: [[Int]]) -> Int {
if connections.count < n - 1 {
return -1
}

var adj = [Int: [Int]]()
for connection in connections {
adj[connection[0], default: []].append(connection[1])
adj[connection[1], default: []].append(connection[0])
}

var numberOfConnectedComponents = 0
var visit = [Bool](repeating: false, count: n)
for i in 0..<n {
if !visit[i] {
numberOfConnectedComponents += 1
dfs(i, adj, &visit)
}
}

return numberOfConnectedComponents - 1
}
}


Ставь 👍 и забирай 📚 Базу знаний
Post #1379 89
Задача: 1427. Perform String Shifts
Сложность: easy

Вам дана строка s, содержащая строчные английские буквы, и матрица shift, где shift[i] = [directioni, amounti]:
directioni может быть 0 (для сдвига влево) или 1 (для сдвига вправо).
amounti - это количество, на которое строка s должна быть сдвинута.
Сдвиг влево на 1 означает удаление первого символа строки s и добавление его в конец.
Аналогично, сдвиг вправо на 1 означает удаление последнего символа строки s и добавление его в начало.
Верните итоговую строку после всех операций.

Пример:
Input: s = "abc", shift = [[0,1],[1,2]]
Output: "cab"
Explanation:
[0,1] means shift to left by 1. "abc" -> "bca"
[1,2] means shift to right by 2. "bca" -> "cab"


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

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

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

3⃣Выполните сдвиг строки на основе вычисленного значения и верните итоговую строку.

😎 Решение:
class Solution {
func stringShift(_ s: String, _ shift: [[Int]]) -> String {
var leftShifts = 0
var rightShifts = 0
for move in shift {
if move[0] == 0 {
leftShifts += move[1]
} else {
rightShifts += move[1]
}
}

let netShifts = (rightShifts - leftShifts) % s.count
let netShift = netShifts < 0 ? s.count + netShifts : netShifts

if netShift == 0 {
return s
}

let splitIndex = s.index(s.startIndex, offsetBy: s.count - netShift)
return String(s[splitIndex...]) + String(s[..<splitIndex])
}
}


Ставь 👍 и забирай 📚 Базу знаний
Post #1378 96
Задача: 720. Longest Word in Dictionary
Сложность: medium

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

Пример:
Input: words = ["w","wo","wor","worl","world"]
Output: "world"


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

1⃣Отсортируйте массив слов по длине и лексикографическому порядку.

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

3⃣Пройдите по каждому слову в отсортированном массиве и добавьте его в множество, если все его префиксы уже существуют в множестве.

😎 Решение:
func longestWord(_ words: [String]) -> String {
let sortedWords = words.sorted()
var validWords: Set<String> = [""]
var longest = ""
for word in sortedWords {
if validWords.contains(String(word.dropLast())) {
validWords.insert(word)
if word.count > longest.count {
longest = word
}
}
}
return longest
}


Ставь 👍 и забирай 📚 Базу знаний
Post #1377 105
Задача: 784. Letter Case Permutation
Сложность: medium

Дан корень дерева поиска (BST). Верните минимальную разницу между значениями любых двух различных узлов в дереве.

Пример:
Input: s = "a1b2"
Output: ["a1b2","a1B2","A1b2","A1B2"]


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

1⃣Если следующий символ c является буквой, то мы удвоим все слова в нашем текущем ответе, и добавим lowercase(c) к каждому слову в первой половине, и uppercase(c) к каждому слову во второй половине.

2⃣Если c является цифрой, мы добавим его к каждому слову.

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

😎 Решение:
class Solution {
func letterCasePermutation(_ S: String) -> [String] {
var ans: [[Character]] = [[]]

for char in S {
let n = ans.count
if char.isLetter {
for i in 0..<n {
ans.append(ans[i])
ans[i].append(char.lowercased().first!)
ans[n+i].append(char.uppercased().first!)
}
} else {
for i in 0..<n {
ans[i].append(char)
}
}
}

return ans.map { String($0) }
}
}


Ставь 👍 и забирай 📚 Базу знаний
Post #1376 100
Задача: 130. Surrounded Regions
Сложность: Medium

Вам дана матрица размером m на n, которая содержит буквы 'X' и 'O'. Захватите регионы, которые окружены:

Соединение: Ячейка соединена с соседними ячейками по горизонтали или вертикали.
Регион: Для формирования региона соедините каждую ячейку 'O'.
Окружение: Регион окружён ячейками 'X', если можно соединить регион с ячейками 'X', и ни одна из ячеек региона не находится на краю доски.
Окруженный регион захватывается путём замены всех 'O' на 'X' в исходной матрице.

Пример:
Input: board = [["X"]]

Output: [["X"]]


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

1⃣Выбор начальных ячеек и инициация DFS:
Начинаем с выбора всех ячеек, расположенных на границах доски.
Затем, начиная с каждой выбранной ячейки на границе, выполняем обход в глубину (DFS).

2⃣Логика и выполнение DFS:
Если ячейка на границе оказывается 'O', это означает, что эта ячейка "жива", вместе с другими ячейками 'O', соединёнными с этой граничной ячейкой. Две ячейки считаются соединёнными, если между ними существует путь, состоящий только из букв 'O'.
Цель нашего обхода DFS будет заключаться в том, чтобы отметить все такие связанные ячейки 'O', которые исходят из границы, каким-либо отличительным символом, например, 'E'.

3⃣Классификация и финальная обработка ячеек:
После обхода всех граничных ячеек мы получаем три типа ячеек:
Ячейки с буквой 'X': эти ячейки можно считать стеной.
Ячейки с буквой 'O': эти ячейки не затрагиваются в нашем обходе DFS, то есть они не имеют соединения с границей, следовательно, они захвачены. Эти ячейки следует заменить на букву 'X'.
Ячейки с буквой 'E': это ячейки, отмеченные в ходе нашего обхода DFS, то есть ячейки, имеющие хотя бы одно соединение с границами, следовательно, они не захвачены. В результате мы должны вернуть этим ячейкам их исходную букву 'O'.

😎 Решение:
class Solution {
private var rows: Int = 0
private var cols: Int = 0

func solve(_ board: inout [[Character]]) {
rows = board.count
guard rows > 0 else { return }
cols = board[0].count
guard cols > 0 else { return }

for i in 0..<rows {
for j in 0..<cols {
if i == 0 || j == 0 || i == rows - 1 || j == cols - 1 {
dfs(&board, i, j)
}
}
}

for i in 0..<rows {
for j in 0..<cols {
if board[i][j] == "O" {
board[i][j] = "X"
} else if board[i][j] == "E" {
board[i][j] = "O"
}
}
}
}

private func dfs(_ board: inout [[Character]], _ i: Int, _ j: Int) {
if board[i][j] != "O" { return }
board[i][j] = "E"
if j < cols - 1 { dfs(&board, i, j + 1) }
if i < rows - 1 { dfs(&board, i + 1, j) }
if j > 0 { dfs(&board, i, j - 1) }
if i > 0 { dfs(&board, i - 1, j) }
}
}


Ставь 👍 и забирай 📚 Базу знаний
Post #1375 91
Задача: CodeTestcaseTest ResultTest Result1187. Make Array Strictly Increasing
Сложность: hard

Даны два целочисленных массива arr1 и arr2. Верните минимальное количество операций (возможно, ноль), необходимых для того, чтобы сделать arr1 строго возрастающим.

В одной операции вы можете выбрать два индекса 0 <= i < arr1.length и 0 <= j < arr2.length и выполнить присваивание arr1[i] = arr2[j].

Если нет способа сделать arr1 строго возрастающим, верните -1.

Пример:
Input: arr1 = [1,5,3,6,7], arr2 = [1,3,2,4]
Output: 1
Explanation: Replace 5 with 2, then arr1 = [1, 2, 3, 6, 7].


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

1⃣Сначала отсортируйте массив arr2 и инициализируйте хэш-таблицу dp для хранения промежуточных результатов. Определите функцию dfs(i, prev), которая вычисляет минимальное количество операций для сортировки массива arr1, начиная с индекса i, при условии, что предыдущий элемент равен prev. Если результат для (i, prev) уже существует в dp, то просто верните сохраненное значение.

2⃣Внутри функции dfs инициализируйте переменную cost значением float('inf'). Если arr1[i] больше, чем prev, обновите значение cost результатом вызова dfs(i + 1, arr1[i]). Используйте бинарный поиск, чтобы найти индекс idx наименьшего значения в arr2, которое больше prev. Если такой индекс существует, обновите значение cost результатом минимального значения между текущим значением cost и 1 + dfs(i + 1, arr2[idx]).

3⃣После всех вычислений обновите dp[(i, prev)] значением cost и верните cost. В конце вызовите dfs(0, -1) и верните его значение, если оно не равно float('inf'); в противном случае верните -1.

😎 Решение
class Solution {
func makeArrayIncreasing(_ arr1: [Int], _ arr2: [Int]) -> Int {
let sortedArr2 = arr2.sorted()
var dp = [String: Int]()

func dfs(_ i: Int, _ prev: Int) -> Int {
if i == arr1.count { return 0 }
let key = "\(i)-\(prev)"
if let val = dp[key] { return val }

var cost = 2001
if arr1[i] > prev {
cost = dfs(i + 1, arr1[i])
}
if let idx = sortedArr2.firstIndex(where: { $0 > prev }) {
cost = min(cost, 1 + dfs(i + 1, sortedArr2[idx]))
}

dp[key] = cost
return cost
}

let result = dfs(0, -1)
return result < 2001 ? result : -1
}
}


Ставь 👍 и забирай 📚 Базу знаний
Post #1374 95
Задача: 1136. Parallel Courses
Сложность: medium

Вам дано целое число n, которое указывает, что есть n курсов, обозначенных от 1 до n. Вам также дан массив relations, где relations[i] = [prevCoursei, nextCoursei], представляющий собой зависимость между курсами: курс prevCoursei должен быть пройден до курса nextCoursei.

За один семестр вы можете взять любое количество курсов, при условии, что вы прошли все необходимые предварительные курсы в предыдущем семестре для тех курсов, которые вы собираетесь взять.
Верните минимальное количество семестров, необходимых для прохождения всех курсов. Если нет способа пройти все курсы, верните -1.

Пример:
Input: n = 3, relations = [[1,3],[2,3]]
Output: 2
Explanation: The figure above represents the given graph.
In the first semester, you can take courses 1 and 2.
In the second semester, you can take course 3.


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

1⃣Постройте направленный граф из зависимостей (relations).

2⃣Реализуйте функцию dfsCheckCycle для проверки наличия цикла в графе.

3⃣Реализуйте функцию dfsMaxPath для вычисления длины самого длинного пути в графе. Если цикл найден, верните -1. Иначе верните длину самого длинного пути.

😎 Решение:
class Solution {
func minimumSemesters(_ N: Int, _ relations: [[Int]]) -> Int {
var graph = [[Int]](repeating: [], count: N + 1)
for relation in relations {
graph[relation[0]].append(relation[1])
}

var visited = [Int](repeating: 0, count: N + 1)
for node in 1...N {
if dfsCheckCycle(node, graph, &visited) == -1 {
return -1
}
}

var visitedLength = [Int](repeating: 0, count: N + 1)
var maxLength = 1
for node in 1...N {
let length = dfsMaxPath(node, graph, &visitedLength)
maxLength = max(length, maxLength)
}
return maxLength
}

private func dfsCheckCycle(_ node: Int, _ graph: [[Int]], _ visited: inout [Int]) -> Int {
if visited[node] != 0 {
return visited[node]
} else {
visited[node] = -1
}
for endNode in graph[node] {
if dfsCheckCycle(endNode, graph, &visited) == -1 {
return -1
}
}
visited[node] = 1
return 1
}

private func dfsMaxPath(_ node: Int, _ graph: [[Int]], _ visitedLength: inout [Int]) -> Int {
if visitedLength[node] != 0 {
return visitedLength[node]
}
var maxLength = 1
for endNode in graph[node] {
let length = dfsMaxPath(endNode, graph, &visitedLength)
maxLength = max(length + 1, maxLength)
}
visitedLength[node] = maxLength
return maxLength
}
}


Ставь 👍 и забирай 📚 Базу знаний
Older posts →
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →