Задача: 25. Reverse Nodes in k-Group #hard
Условие:
Решение:
Пояснение:
1. Определение структуры ListNode:
- Класс ListNode представляет узел связанного списка с полями для значения (val) и указателя на следующий узел (next).
2. Инициализация фиктивного узла (dummy):
- Создаем фиктивный узел для упрощения обработки головы списка и устанавливаем его next на текущую голову спис
Определение длины спискасписка:
- Сначала определяем длину списка, чтобы знать, сколько полных групп по k узлов можно переставить.
4. Перестановка узлов по k за раз:
- Пока оставшихся узлов хватает для полной группы по k узлов:
- Устанавливаем current на первый узел текущей группы, а next на второй узел.
- Перемещаем узлы в группе, изменяя указатели так, чтобы узлы были переставлены в обратном порядке.
- После перестановки обновляем prev для перехода к следующей группе.
- Уменьшаем count на k, так как мы уже обработали одну Возвращение головы нового спискаого списка:
- Возвращаем dummy.next, который теперь указывает на новую голову списка после всех перестановок.
Пример использования
В примере создается связанный список 1 -> 2 -> 3 -> 4 -> 5. После вызова функции reverseKGroup с k = 3, узлы будут переставлены группами по три: 3 -> 2 -> 1 -> 4 -> 5. Результат выводится в консоль..
Условие:
Учитывая заголовок связанного списка, поменяйте местами узлы списка k за раз и верните измененный список.
k — целое положительное число, меньшее или равное длине связанного списка. Если количество узлов не кратно k, то пропущенные узлы в конечном итоге должны остаться такими, какие они есть.
Вы не можете изменять значения в узлах списка, можно изменять только сами узлы.
Решение:
class ListNode {
var val: Int
var next: ListNode?
init(_ val: Int) {
self.val = val
self.next = nil
}
}
func reverseKGroup(_ head: ListNode?, _ k: Int) -> ListNode? {
if head == nil || k == 1 {
return head
}
let dummy = ListNode(0)
dummy.next = head
var current: ListNode? = dummy
var next: ListNode?
var prev: ListNode? = dummy
// Определяем длину списка
var count = 0
while current?.next != nil {
current = current?.next
count += 1
}
while count >= k {
current = prev?.next
next = current?.next
for _ in 1..<k {
current?.next = next?.next
next?.next = prev?.next
prev?.next = next
next = current?.next
}
prev = current
count -= k
}
return dummy.next
}
// Пример использования
let node1 = ListNode(1)
let node2 = ListNode(2)
let node3 = ListNode(3)
let node4 = ListNode(4)
let node5 = ListNode(5)
node1.next = node2
node2.next = node3
node3.next = node4
node4.next = node5
if let reversedList = reverseKGroup(node1, 3) {
var current: ListNode? = reversedList
while current != nil {
print(current!.val, terminator: " ")
current = current?.next
}
}Пояснение:
1. Определение структуры ListNode:
- Класс ListNode представляет узел связанного списка с полями для значения (val) и указателя на следующий узел (next).
2. Инициализация фиктивного узла (dummy):
- Создаем фиктивный узел для упрощения обработки головы списка и устанавливаем его next на текущую голову спис
Определение длины спискасписка:
- Сначала определяем длину списка, чтобы знать, сколько полных групп по k узлов можно переставить.
4. Перестановка узлов по k за раз:
- Пока оставшихся узлов хватает для полной группы по k узлов:
- Устанавливаем current на первый узел текущей группы, а next на второй узел.
- Перемещаем узлы в группе, изменяя указатели так, чтобы узлы были переставлены в обратном порядке.
- После перестановки обновляем prev для перехода к следующей группе.
- Уменьшаем count на k, так как мы уже обработали одну Возвращение головы нового спискаого списка:
- Возвращаем dummy.next, который теперь указывает на новую голову списка после всех перестановок.
Пример использования
В примере создается связанный список 1 -> 2 -> 3 -> 4 -> 5. После вызова функции reverseKGroup с k = 3, узлы будут переставлены группами по три: 3 -> 2 -> 1 -> 4 -> 5. Результат выводится в консоль..
❤1
Задача: 25. Reverse Nodes in k-Group #hard
Условие:
Решение:
Пояснение:
Условие:
Учитывая заголовок связанного списка, поменяйте местами узлы списка k за раз и верните измененный список.
k — целое положительное число, меньшее или равное длине связанного списка. Если количество узлов не кратно k, то пропущенные узлы в конечном итоге должны остаться такими, какие они есть.
Вы не можете изменять значения в узлах списка, можно изменять только сами узлы.
Решение:
class ListNode {
var val: Int
var next: ListNode?
init(_ val: Int) {
self.val = val
self.next = nil
}
}
func reverseKGroup(_ head: ListNode?, _ k: Int) -> ListNode? {
if head == nil || k == 1 {
return head
}
let dummy = ListNode(0)
dummy.next = head
var current: ListNode? = dummy
var next: ListNode?
var prev: ListNode? = dummy
// Определяем длину списка
var count = 0
while current?.next != nil {
current = current?.next
count += 1
}
while count >= k {
current = prev?.next
next = current?.next
for _ in 1..<k {
current?.next = next?.next
next?.next = prev?.next
prev?.next = next
next = current?.next
}
prev = current
count -= k
}
return dummy.next
}
// Пример использования
let node1 = ListNode(1)
let node2 = ListNode(2)
let node3 = ListNode(3)
let node4 = ListNode(4)
let node5 = ListNode(5)
node1.next = node2
node2.next = node3
node3.next = node4
node4.next = node5
if let reversedList = reverseKGroup(node1, 3) {
var current: ListNode? = reversedList
while current != nil {
print(current!.val, terminator: " ")
current = current?.next
}
}Пояснение:
1. Определение структурыListNode:
- КлассListNodeпредставляет узел связанного списка с полями для значения (val) и указателя на следующий узел (next).
2. Инициализация фиктивного узла (dummy):
- Создаем фиктивный узел для упрощения обработки головы списка и устанавливаем егоnextна текущую голову спис
Определение длины спискасписка:
- Сначала определяем длину списка, чтобы знать, сколько полных групп поkузлов можно переставить.
4. Перестановка узлов поkза раз:
- Пока оставшихся узлов хватает для полной группы поkузлов:
- Устанавливаемcurrentна первый узел текущей группы, аnextна второй узел.
- Перемещаем узлы в группе, изменяя указатели так, чтобы узлы были переставлены в обратном порядке.
- После перестановки обновляемprevдля перехода к следующей группе.
- Уменьшаемcountнаk, так как мы уже обработали одну Возвращение головы нового спискаого списка:
- Возвращаемdummy.next, который теперь указывает на новую голову списка после всех перестановок.
Пример использования
В примере создается связанный список 1 -> 2 -> 3 -> 4 -> 5. После вызова функцииreverseKGroupсk = 3, узлы будут переставлены группами по три: 3 -> 2 -> 1 -> 4 -> 5. Результат выводится в консоль..
❤1
Задача: 26. Remove Duplicates from Sorted Array #easy
Условие:
Решение:
Пояснение:
- В функции removeDuplicates используется переменная k для отслеживания текущей позиции для уникальных элементов.
- Мы проходим по массиву, начиная с индекса 1, и сравниваем текущий элемент с предыдущим (на позиции k-1).
- Если текущий элемент отличается от предыдущего, мы помещаем его на позицию k и увеличиваем k.
- В конце функции возвращаем k, который содержит количество уникальных элементов.
- Пример использования показывает, как после удаления дубликатов в массиве nums получается массив с уникальными элементами и выводится их количество.
Условие:
Учитывая целочисленный массив чисел, отсортированный в неубывающем порядке, удалите дубликаты на месте так, чтобы каждый уникальный элемент появлялся только один раз. Относительный порядок элементов должен оставаться неизменным. Затем верните количество уникальных элементов в числах.
Считайте, что количество уникальных элементов чисел равно k. Чтобы вас приняли, вам нужно сделать следующее:
Измените массив nums так, чтобы первые k элементов nums содержали уникальные элементы в том порядке, в котором они присутствовали в nums изначально. Остальные элементы nums не важны, как и размер nums.
Вернуть К.
Решение:
func removeDuplicates(_ nums: inout [Int]) -> Int {
if nums.isEmpty { return 0 }
var k = 1 // Индекс для уникальных элементов
for i in 1..<nums.count {
if nums[i] != nums[k - 1] {
nums[k] = nums[i]
k += 1
}
}
return k
}
// Пример использования
var nums = [1, 1, 2, 2, 2, 3, 4, 4, 5]
let k = removeDuplicates(&nums)
print("Количество уникальных элементов:", k) // Вывод: Количество уникальных элементов: 5
print("Массив с уникальными элементами:", nums.prefix(k)) // Вывод: Массив с уникальными элементами: [1, 2, 3, 4, 5]Пояснение:
- В функции removeDuplicates используется переменная k для отслеживания текущей позиции для уникальных элементов.
- Мы проходим по массиву, начиная с индекса 1, и сравниваем текущий элемент с предыдущим (на позиции k-1).
- Если текущий элемент отличается от предыдущего, мы помещаем его на позицию k и увеличиваем k.
- В конце функции возвращаем k, который содержит количество уникальных элементов.
- Пример использования показывает, как после удаления дубликатов в массиве nums получается массив с уникальными элементами и выводится их количество.
❤1
Задача: 27. Remove Element #easy
Условие:
Решение:
1. Функция removeElement:
- Принимает массив nums и значение val, которое нужно удалить.
- Инициализирует переменную k для отслеживания индекса элементов, которые не равны val.
- Проходит по массиву nums и для каждого элемента, который не равен val, помещает его на позицию k и увеличивает k.
- Возвращает k, который представляет количество элементов, не равных val.
2. Пример использования:
- Создается массив nums с элементами [3, 2, 2, 3] и значение val = 3.
- Функция вызывается с этими параметрами, и результат (количество элементов, не равных val и измененный массив nums) выводится на экран.
Этот код эффективно решает задачу удаления всех вхождений значения val из массива nums на месте и возвращает количество элементов, которые не равны val, что соответствует требованиям задачи.
Условие:
Учитывая целочисленный массив nums и целочисленное значение, удалите все вхождения val в nums на месте. Порядок элементов может быть изменен. Затем верните количество элементов в виде чисел, которые не равны val.
Учитывайте количество элементов в nums, которые не равны val be k. Чтобы вас приняли, вам необходимо сделать следующее:
Измените массив nums так, чтобы первые k элементов nums содержали элементы, не равные val. Остальные элементы nums не важны, как и размер nums.
Вернуть К.
Решение:
func removeElement(_ nums: inout [Int], _ val: Int) -> Int {
var k = 0 // Индекс для элементов, не равных val
for i in 0..<nums.count {
if nums[i] != val {
nums[k] = nums[i]
k += 1
}
}
return k
}
// Пример использования
var nums = [3, 2, 2, 3]
let val = 3
let k = removeElement(&nums, val)
print("Количество элементов, не равных \(val):", k) // Вывод: Количество элементов, не равных 3: 2
print("Массив с элементами, не равными \(val):", nums.prefix(k)) // Вывод: Массив с элементами, не равными 3: [2, 2]Пояснение:1. Функция removeElement:
- Принимает массив nums и значение val, которое нужно удалить.
- Инициализирует переменную k для отслеживания индекса элементов, которые не равны val.
- Проходит по массиву nums и для каждого элемента, который не равен val, помещает его на позицию k и увеличивает k.
- Возвращает k, который представляет количество элементов, не равных val.
2. Пример использования:
- Создается массив nums с элементами [3, 2, 2, 3] и значение val = 3.
- Функция вызывается с этими параметрами, и результат (количество элементов, не равных val и измененный массив nums) выводится на экран.
Этот код эффективно решает задачу удаления всех вхождений значения val из массива nums на месте и возвращает количество элементов, которые не равны val, что соответствует требованиям задачи.
❤1
Задача: 26. Remove Duplicates from Sorted Array #easy
Условие:
Решение:
Пояснение:
Условие:
Учитывая целочисленный массив чисел, отсортированный в неубывающем порядке, удалите дубликаты на месте так, чтобы каждый уникальный элемент появлялся только один раз. Относительный порядок элементов должен оставаться неизменным. Затем верните количество уникальных элементов в числах.
Считайте, что количество уникальных элементов чисел равно k. Чтобы вас приняли, вам нужно сделать следующее:
Измените массив nums так, чтобы первые k элементов nums содержали уникальные элементы в том порядке, в котором они присутствовали в nums изначально. Остальные элементы nums не важны, как и размер nums.
Вернуть К.
Решение:
func removeDuplicates(_ nums: inout [Int]) -> Int {
if nums.isEmpty { return 0 }
var k = 1 // Индекс для уникальных элементов
for i in 1..<nums.count {
if nums[i] != nums[k - 1] {
nums[k] = nums[i]
k += 1
}
}
return k
}
// Пример использования
var nums = [1, 1, 2, 2, 2, 3, 4, 4, 5]
let k = removeDuplicates(&nums)
print("Количество уникальных элементов:", k) // Вывод: Количество уникальных элементов: 5
print("Массив с уникальными элементами:", nums.prefix(k)) // Вывод: Массив с уникальными элементами: [1, 2, 3, 4, 5]Пояснение:
- В функции removeDuplicates используется переменная k для отслеживания текущей позиции для уникальных элементов.
- Мы проходим по массиву, начиная с индекса 1, и сравниваем текущий элемент с предыдущим (на позиции k-1).
- Если текущий элемент отличается от предыдущего, мы помещаем его на позицию k и увеличиваем k.
- В конце функции возвращаем k, который содержит количество уникальных элементов.
- Пример использования показывает, как после удаления дубликатов в массиве nums получается массив с уникальными элементами и выводится их количество.
❤1
Задача: 28. Find the Index of the First Occurrence in a String #easy
Условие:
Решение:
Пояснение:
1. Функция findNeedleInHaystack:
- Принимает две строки: haystack (стог сена) и needle (игла).
- Использует метод range(of:) для поиска первого вхождения подстроки needle в строке haystack.
- Если подстрока найдена (range не равен nil), вычисляется индекс первого символа подстроки needle в строке haystack с помощью метода distance(from:to:).
- Возвращается найденный индекс.
- Если подстрока не найдена, возвращается -1.
Пример использованияия:
- Создается строка haystack с текстом "стог сена".
- Создается строка needle с текстом "игла".
- Функция вызывается с этими строками, и результат (индекс или -1) выводится на экран.
Этот код эффективно находит первое вхождение подстроки в строке, сохраняя относительный порядок символов и возвращая соответствующий индекс или -1, если подстрока не найдена.
Условие:
Учитывая две строки, игла и стог сена, верните индекс первого вхождения иглы в стоге сена или -1, если игла не является частью стога сена.
Решение:
func findNeedleInHaystack(_ haystack: String, _ needle: String) -> Int {
if let range = haystack.range(of: needle) {
return haystack.distance(from: haystack.startIndex, to: range.lowerBound)
} else {
return -1
}
}
// Пример использования
let haystack = "стог сена"
let needle = "игла"
let index = findNeedleInHaystack(haystack, needle)
print("Индекс первого вхождения иглы в стог сена:", index)Пояснение:
1. Функция findNeedleInHaystack:
- Принимает две строки: haystack (стог сена) и needle (игла).
- Использует метод range(of:) для поиска первого вхождения подстроки needle в строке haystack.
- Если подстрока найдена (range не равен nil), вычисляется индекс первого символа подстроки needle в строке haystack с помощью метода distance(from:to:).
- Возвращается найденный индекс.
- Если подстрока не найдена, возвращается -1.
Пример использованияия:
- Создается строка haystack с текстом "стог сена".
- Создается строка needle с текстом "игла".
- Функция вызывается с этими строками, и результат (индекс или -1) выводится на экран.
Этот код эффективно находит первое вхождение подстроки в строке, сохраняя относительный порядок символов и возвращая соответствующий индекс или -1, если подстрока не найдена.
❤1
Задача: 29. Divide Two Integers #medium
Условие:
Решение:
Пояснение:
1. Особые случаи:
- Проверяем случаи деления на ноль или когда делимое равно Int.min, а делитель равен -1, что приведет к переполнению. В таких случаях возвращаем максимальное значение Int.
2. Определение знака результата:
- Определяем знак результата деления на основе знаков делимого и делителя.
3. Приведение к абсолютным значениям:
- Приводим числа к абсолютным значениям для удобства работы и чтобы избежать проблем с переполнением.
4. Цикл деления путем вычитания:
- Используем цикл, в котором делимое вычитается наибольшими возможными кратными значениями делителя, умноженными на степень двойки (tempDivisor <<= 1).
5. Применение знака и проверка на переполнение:
- Применяем знак к результату, затем проверяем, не превышает ли результат допустимый диапазон Int32. Если превышает, возвращаем максимальное или минимальное значение Int32.
Этот подход эффективно решает задачу деления целых чисел без использования операторов умножения, деления и модуля, с учетом всех особых случаев и ограничений задачи.
Условие:
Учитывая два целых числа: делимое и делитель, разделите два целых числа, не используя операторы умножения, деления и модификатора.
Целочисленное деление должно сокращаться до нуля, что означает потерю дробной части. Например, 8,345 будет сокращено до 8, а -2,7335 будет сокращено до -2.
Возвращает частное после деления делимого на делитель.
Примечание. Предположим, мы имеем дело со средой, которая может хранить целые числа только в пределах 32-битного целого диапазона со знаком: [−2^31, 2^31 − 1]. В этой задаче, если частное строго больше 2^31 - 1, верните 2^31 - 1, а если частное строго меньше -2^31, верните -2^31.
Решение:
func divide(_ dividend: Int, _ divisor: Int) -> Int {
// Особые случаи: деление на ноль или делитель равен нулю
if divisor == 0 || (dividend == Int.min && divisor == -1) {
return Int.max
}
// Определение знака результата
let isNegative = (dividend < 0) != (divisor < 0)
// Приведение чисел к абсолютным значениям и приведение их к типу Int64,
// чтобы избежать переполнения при работе с Int32.
var absDividend = abs(Int64(dividend))
let absDivisor = abs(Int64(divisor))
// Инициализация результата
var result: Int64 = 0
// Выполнение деления путем вычитания
while absDividend >= absDivisor {
var tempDivisor = absDivisor
var multiple: Int64 = 1
// Увеличиваем делитель в два раза, пока он не станет больше чем делимое
while absDividend >= (tempDivisor << 1) {
tempDivisor <<= 1
multiple <<= 1
}
// Вычитаем делитель из делимого и обновляем результат
absDividend -= tempDivisor
result += multiple
}
// Применяем знак к результату и проверяем на переполнение
if isNegative {
result = -result
}
if result > Int64(Int32.max) {
return Int(Int32.max)
} else if result < Int64(Int32.min) {
return Int(Int32.min)
} else {
return Int(result)
}
}
// Пример использования
let dividend = 10
let divisor = 3
let quotient = divide(dividend, divisor)
print("Частное деления \(dividend) на \(divisor) равно \(quotient)") Пояснение:
1. Особые случаи:
- Проверяем случаи деления на ноль или когда делимое равно Int.min, а делитель равен -1, что приведет к переполнению. В таких случаях возвращаем максимальное значение Int.
2. Определение знака результата:
- Определяем знак результата деления на основе знаков делимого и делителя.
3. Приведение к абсолютным значениям:
- Приводим числа к абсолютным значениям для удобства работы и чтобы избежать проблем с переполнением.
4. Цикл деления путем вычитания:
- Используем цикл, в котором делимое вычитается наибольшими возможными кратными значениями делителя, умноженными на степень двойки (tempDivisor <<= 1).
5. Применение знака и проверка на переполнение:
- Применяем знак к результату, затем проверяем, не превышает ли результат допустимый диапазон Int32. Если превышает, возвращаем максимальное или минимальное значение Int32.
Этот подход эффективно решает задачу деления целых чисел без использования операторов умножения, деления и модуля, с учетом всех особых случаев и ограничений задачи.
❤1
Задача: 27. Remove Element #easy
Условие:
Решение:
Условие:
Учитывая целочисленный массив nums и целочисленное значение, удалите все вхождения val в nums на месте. Порядок элементов может быть изменен. Затем верните количество элементов в виде чисел, которые не равны val.
Учитывайте количество элементов в nums, которые не равны val be k. Чтобы вас приняли, вам необходимо сделать следующее:
Измените массив nums так, чтобы первые k элементов nums содержали элементы, не равные val. Остальные элементы nums не важны, как и размер nums.
Вернуть К.
Решение:
func removeElement(_ nums: inout [Int], _ val: Int) -> Int {
var k = 0 // Индекс для элементов, не равных val
for i in 0..<nums.count {
if nums[i] != val {
nums[k] = nums[i]
k += 1
}
}
return k
}
// Пример использования
var nums = [3, 2, 2, 3]
let val = 3
let k = removeElement(&nums, val)
print("Количество элементов, не равных \(val):", k) // Вывод: Количество элементов, не равных 3: 2
print("Массив с элементами, не равными \(val):", nums.prefix(k)) // Вывод: Массив с элементами, не равными 3: [2, 2]Пояснение:1. Функция removeElement:
- Принимает массив nums и значение val, которое нужно удалить.
- Инициализирует переменную k для отслеживания индекса элементов, которые не равны val.
- Проходит по массиву nums и для каждого элемента, который не равен val, помещает его на позицию k и увеличивает k.
- Возвращает k, который представляет количество элементов, не равных val.
2. Пример использования:
- Создается массив nums с элементами [3, 2, 2, 3] и значение val = 3.
- Функция вызывается с этими параметрами, и результат (количество элементов, не равных val и измененный массив nums) выводится на экран.
Этот код эффективно решает задачу удаления всех вхождений значения val из массива nums на месте и возвращает количество элементов, которые не равны val, что соответствует требованиям задачи.
❤1
Задача: 30. Substring with Concatenation of All Words #hard
Условие:
Решение:
Пояснение:
Данный код реализует функцию `findSubstring`, которая ищет все подстроки в строке `s`, которые содержат все слова из массива `words`. Каждое слово из массива `words` имеет фиксированную длину, которая определяется длиной первого слова в массиве.
Основные шаги алгоритма:
1. Проверяется, что длина строки `s` больше или равна произведению длины слова на количество слов в массиве `words`. Если это не так, возвращается пустой массив.
2. Входная строка `s` и массив слов `words` преобразуются в массивы символов, чтобы облегчить обращение к символам.
3. Создается словарь `wf`, который содержит количество вхождений каждого слова из массива `words`.
4. Создается множество `ws`, содержащее все слова из массива `words`.
5. Для каждой позиции `i` в строке `s`, начиная с 0 и до `s.count - cnt * len`:
- Сначала создается пустой словарь `sf`, который будет хранить количество вхождений каждого слова на текущей позиции.
- Устанавливается переменная `j` равной `i`.
- Далее, для каждого слова в массиве `words`:
- Извлекается подстрока длиной `len` начиная с позиции `j`.
- Проверяется, содержится ли данное слово в множестве `ws`. Если нет, происходит выход из цикла.
- Проверяется, что количество вхождений данного слова не больше, чем его общее количество в `wf`. Если условие не выполняется, происходит выход из цикла.
- Увеличивается количество вхождений данного слова в словаре `sf`.
- Переменная `j` увеличивается на длину слова.
- Если переменная `j` стала равной `i + cnt * len` и словарь `sf` равен словарю `wf`, то индекс `i` добавляется в массив результатов.
6. Возвращается массив результатов `res`.
Этот алгоритм эффективно находит все подстроки в строке `s`, содержащие все слова из массива `words`. Сложность алгоритма зависит от длины строки `s`, длины слова, числа слов в массиве `words` и общего числа символов в `s`.
Условие:
Вам дана строка s и массив строк-слов. Все строки слов имеют одинаковую длину.
Объединенная строка — это строка, которая в точности содержит все строки любой перестановки объединенных слов.
Например, если слова = ["ab", "cd", "ef"], то "abcdef", "abefcd", "cdabef", "cdefab", "efabcd" и "efcdab" являются объединенными строками. «acdbef» не является объединенной строкой, поскольку не является объединением какой-либо перестановки слов.
Возвращает массив начальных индексов всех объединенных подстрок в s. Вы можете вернуть ответ в любом порядке.
Решение:
class Solution {
func findSubstring(_ s: String, _ words: [String]) -> [Int] {
let len = words.first!.count
let cnt = words.count
guard s.count >= cnt * len else { return [] }
let s = Array(s)
let words = words.map(Array.init)
let wf = words.reduce(into: [[Character]: Int]()) { $0[$1, default: 0] += 1 }
let ws = Set(words)
var res = [Int]()
for i in 0...(s.count - cnt * len) {
var sf = [[Character]: Int]()
var j = i
for _ in 0..<cnt {
let w = Array(s[j..<j + len])
guard ws.contains(w) else { break }
let old = sf[w, default: 0]
guard old + 1 <= wf[w]! else { break }
sf[w] = old + 1
j += len
}
guard j == i + cnt * len, sf == wf else { continue }
res.append(i)
}
return res
}
}Пояснение:
Данный код реализует функцию `findSubstring`, которая ищет все подстроки в строке `s`, которые содержат все слова из массива `words`. Каждое слово из массива `words` имеет фиксированную длину, которая определяется длиной первого слова в массиве.
Основные шаги алгоритма:
1. Проверяется, что длина строки `s` больше или равна произведению длины слова на количество слов в массиве `words`. Если это не так, возвращается пустой массив.
2. Входная строка `s` и массив слов `words` преобразуются в массивы символов, чтобы облегчить обращение к символам.
3. Создается словарь `wf`, который содержит количество вхождений каждого слова из массива `words`.
4. Создается множество `ws`, содержащее все слова из массива `words`.
5. Для каждой позиции `i` в строке `s`, начиная с 0 и до `s.count - cnt * len`:
- Сначала создается пустой словарь `sf`, который будет хранить количество вхождений каждого слова на текущей позиции.
- Устанавливается переменная `j` равной `i`.
- Далее, для каждого слова в массиве `words`:
- Извлекается подстрока длиной `len` начиная с позиции `j`.
- Проверяется, содержится ли данное слово в множестве `ws`. Если нет, происходит выход из цикла.
- Проверяется, что количество вхождений данного слова не больше, чем его общее количество в `wf`. Если условие не выполняется, происходит выход из цикла.
- Увеличивается количество вхождений данного слова в словаре `sf`.
- Переменная `j` увеличивается на длину слова.
- Если переменная `j` стала равной `i + cnt * len` и словарь `sf` равен словарю `wf`, то индекс `i` добавляется в массив результатов.
6. Возвращается массив результатов `res`.
Этот алгоритм эффективно находит все подстроки в строке `s`, содержащие все слова из массива `words`. Сложность алгоритма зависит от длины строки `s`, длины слова, числа слов в массиве `words` и общего числа символов в `s`.
❤1
#medium
Задача: 31. Next Permutation
Перестановка массива целых чисел — это упорядочивание его элементов в последовательность или линейный порядок.
Например, для arr = [1,2,3] следующие являются всеми перестановками arr: [1,2,3], [1,3,2], [2, 1, 3], [2, 3, 1], [3,1,2], [3,2,1].
Следующая перестановка массива целых чисел — это следующая лексикографически большая перестановка его чисел. Более формально, если все перестановки массива отсортированы в одном контейнере по лексикографическому порядку, то следующая перестановка этого массива — это перестановка, следующая за ней в отсортированном контейнере. Если такое упорядочивание невозможно, массив должен быть переупорядочен в наименьший возможный порядок (то есть отсортирован по возрастанию).
Например, следующая перестановка arr = [1,2,3] — это [1,3,2].
Аналогично, следующая перестановка arr = [2,3,1] — это [3,1,2].
В то время как следующая перестановка arr = [3,2,1] — это [1,2,3], потому что [3,2,1] не имеет лексикографически большего переупорядочивания.
Для данного массива целых чисел nums найдите следующую перестановку nums.
Замена должна быть выполнена на месте и использовать только постоянную дополнительную память.
Пример:
👨💻 Алгоритм:
1️⃣ Мы меняем местами числа a[i−1] и a[j]. Теперь у нас есть правильное число на индексе i−1. Однако текущая перестановка ещё не является той перестановкой, которую мы ищем. Нам нужна наименьшая перестановка, которая может быть сформирована с использованием чисел только справа от a[i−1]. Следовательно, нам нужно расположить эти числа в порядке возрастания, чтобы получить их наименьшую перестановку.
2️⃣ Однако, вспомните, что, сканируя числа справа налево, мы просто уменьшали индекс, пока не нашли пару a[i] и a[i−1], где a[i] > a[i−1]. Таким образом, все числа справа от a[i−1] уже были отсортированы в порядке убывания. Более того, обмен местами a[i−1] и a[j] не изменил этот порядок.
3️⃣ Поэтому нам просто нужно перевернуть числа, следующие за a[i−1], чтобы получить следующую наименьшую лексикографическую перестановку.
😎 Решение:
🪙 823 вопроса вопроса на iOS разработчика
🔒 База собесов | 🔒 База тестовых
Задача: 31. Next Permutation
Перестановка массива целых чисел — это упорядочивание его элементов в последовательность или линейный порядок.
Например, для arr = [1,2,3] следующие являются всеми перестановками arr: [1,2,3], [1,3,2], [2, 1, 3], [2, 3, 1], [3,1,2], [3,2,1].
Следующая перестановка массива целых чисел — это следующая лексикографически большая перестановка его чисел. Более формально, если все перестановки массива отсортированы в одном контейнере по лексикографическому порядку, то следующая перестановка этого массива — это перестановка, следующая за ней в отсортированном контейнере. Если такое упорядочивание невозможно, массив должен быть переупорядочен в наименьший возможный порядок (то есть отсортирован по возрастанию).
Например, следующая перестановка arr = [1,2,3] — это [1,3,2].
Аналогично, следующая перестановка arr = [2,3,1] — это [3,1,2].
В то время как следующая перестановка arr = [3,2,1] — это [1,2,3], потому что [3,2,1] не имеет лексикографически большего переупорядочивания.
Для данного массива целых чисел nums найдите следующую перестановку nums.
Замена должна быть выполнена на месте и использовать только постоянную дополнительную память.
Пример:
Input: nums = [1,2,3]
Output: [1,3,2]
func nextPermutation(_ nums: inout [Int]) {
var i = nums.count - 2
while i >= 0 && nums[i + 1] <= nums[i] {
i -= 1
}
if i >= 0 {
var j = nums.count - 1
while nums[j] <= nums[i] {
j -= 1
}
nums.swapAt(i, j)
}
reverse(nums: &nums, start: i + 1)
}
func reverse(nums: inout [Int], start: Int) {
var i = start
var j = nums.count - 1
while i < j {
nums.swapAt(i, j)
i += 1
j -= 1
}
}Please open Telegram to view this post
VIEW IN TELEGRAM
❤1
Задача: 28. Find the Index of the First Occurrence in a String #easy
Условие:
Решение:
Пояснение:
Условие:
Учитывая две строки, игла и стог сена, верните индекс первого вхождения иглы в стоге сена или -1, если игла не является частью стога сена.
Решение:
func findNeedleInHaystack(_ haystack: String, _ needle: String) -> Int {
if let range = haystack.range(of: needle) {
return haystack.distance(from: haystack.startIndex, to: range.lowerBound)
} else {
return -1
}
}
// Пример использования
let haystack = "стог сена"
let needle = "игла"
let index = findNeedleInHaystack(haystack, needle)
print("Индекс первого вхождения иглы в стог сена:", index)Пояснение:
1. Функция findNeedleInHaystack:
- Принимает две строки: haystack (стог сена) и needle (игла).
- Использует метод range(of:) для поиска первого вхождения подстроки needle в строке haystack.
- Если подстрока найдена (range не равен nil), вычисляется индекс первого символа подстроки needle в строке haystack с помощью метода distance(from:to:).
- Возвращается найденный индекс.
- Если подстрока не найдена, возвращается -1.
Пример использованияия:
- Создается строка haystack с текстом "стог сена".
- Создается строка needle с текстом "игла".
- Функция вызывается с этими строками, и результат (индекс или -1) выводится на экран.
Этот код эффективно находит первое вхождение подстроки в строке, сохраняя относительный порядок символов и возвращая соответствующий индекс или -1, если подстрока не найдена.
❤1
#hard
Задача: 32. Longest Valid Parentheses
Дана строка, содержащая только символы '(' и ')'. Верните длину самой длинной подстроки с корректными (правильно сформированными) скобками.
Пример:
👨💻 Алгоритм:
1️⃣ В этом подходе мы рассматриваем каждую возможную непустую подстроку чётной длины из заданной строки и проверяем, является ли она корректной строкой скобок. Для проверки корректности мы используем метод стека.
2️⃣ Каждый раз, когда мы встречаем символ ‘(’, мы кладём его в стек. Для каждого встреченного символа ‘)’ мы извлекаем из стека символ ‘(’. Если символ ‘(’ недоступен в стеке для извлечения в любой момент или если в стеке остались элементы после обработки всей подстроки, подстрока скобок является некорректной.
3️⃣ Таким образом, мы повторяем процесс для каждой возможной подстроки и продолжаем сохранять длину самой длинной найденной корректной строки.
😎 Решение:
🪙 823 вопроса вопроса на iOS разработчика
🔒 База собесов | 🔒 База тестовых
Задача: 32. Longest Valid Parentheses
Дана строка, содержащая только символы '(' и ')'. Верните длину самой длинной подстроки с корректными (правильно сформированными) скобками.
Пример:
Input: s = "(()"
Output: 2
func isValid(_ s: String) -> Bool {
var stack = [Character]()
for char in s {
if char == "(" {
stack.append("(")
} else if !stack.isEmpty && stack.last == "(" {
stack.removeLast()
} else {
return false
}
}
return stack.isEmpty
}
func longestValidParentheses(_ s: String) -> Int {
var maxlen = 0
for i in 0..<s.count {
for j in stride(from: i + 2, through: s.count, by: 2) {
let start = s.index(s.startIndex, offsetBy: i)
let end = s.index(s.startIndex, offsetBy: j)
let substring = String(s[start..<end])
if isValid(substring) {
maxlen = max(maxlen, j - i)
}
}
}
return maxlen
}Please open Telegram to view this post
VIEW IN TELEGRAM
❤1
#medium
Задача: 33. Search in Rotated Sorted Array
Есть массив целых чисел nums, отсортированный в порядке возрастания (с уникальными значениями).
Перед передачей в вашу функцию массив nums может быть повёрнут в неизвестном индексе поворота k (1 <= k < nums.length), так что результирующий массив будет иметь вид [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]] (с индексацией с нуля). Например, [0,1,2,4,5,6,7] может быть повёрнут в индексе поворота 3 и стать [4,5,6,7,0,1,2].
Для данного массива nums после возможного поворота и целого числа target, верните индекс target, если он есть в массиве, или -1, если его нет в массиве.
Вы должны написать алгоритм с временной сложностью O(log n).
Пример:
👨💻 Алгоритм:
1️⃣ Выполните двоичный поиск для определения индекса поворота, инициализируя границы области поиска значениями left = 0 и right = n - 1. Пока left < right:
Пусть mid = left + (right - left) // 2.
Если nums[mid] > nums[n - 1], это предполагает, что точка поворота находится справа от mid, следовательно, мы устанавливаем left = mid + 1. В противном случае, поворот может находиться на позиции mid или левее от mid, в этом случае мы должны установить right = mid.
2️⃣ По завершении двоичного поиска мы имеем индекс поворота, обозначенный как pivot = left.
nums состоит из двух отсортированных подмассивов, nums[0 ~ left - 1] и nums[left ~ n - 1].
3️⃣ Выполните двоичный поиск по подмассиву nums[0 ~ left - 1] для поиска target. Если target находится в этом подмассиве, верните его индекс.
В противном случае выполните двоичный поиск по подмассиву nums[left ~ n - 1] для поиска target. Если target находится в этом подмассиве, верните его индекс. В противном случае верните -1.
😎 Решение:
🪙 823 вопроса вопроса на iOS разработчика
🔒 База собесов | 🔒 База тестовых
Задача: 33. Search in Rotated Sorted Array
Есть массив целых чисел nums, отсортированный в порядке возрастания (с уникальными значениями).
Перед передачей в вашу функцию массив nums может быть повёрнут в неизвестном индексе поворота k (1 <= k < nums.length), так что результирующий массив будет иметь вид [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]] (с индексацией с нуля). Например, [0,1,2,4,5,6,7] может быть повёрнут в индексе поворота 3 и стать [4,5,6,7,0,1,2].
Для данного массива nums после возможного поворота и целого числа target, верните индекс target, если он есть в массиве, или -1, если его нет в массиве.
Вы должны написать алгоритм с временной сложностью O(log n).
Пример:
Input: nums = [4,5,6,7,0,1,2], target = 0
Output: 4
Пусть mid = left + (right - left) // 2.
Если nums[mid] > nums[n - 1], это предполагает, что точка поворота находится справа от mid, следовательно, мы устанавливаем left = mid + 1. В противном случае, поворот может находиться на позиции mid или левее от mid, в этом случае мы должны установить right = mid.
nums состоит из двух отсортированных подмассивов, nums[0 ~ left - 1] и nums[left ~ n - 1].
В противном случае выполните двоичный поиск по подмассиву nums[left ~ n - 1] для поиска target. Если target находится в этом подмассиве, верните его индекс. В противном случае верните -1.
func search(_ nums: [Int], _ target: Int) -> Int {
let n = nums.count
var left = 0
var right = n - 1
// Find the index of the pivot element (the smallest element)
while left <= right {
let mid = (left + right) / 2
if nums[mid] > nums[n - 1] {
left = mid + 1
} else {
right = mid - 1
}
}
func binarySearch(_ leftBoundary: Int, _ rightBoundary: Int, _ target: Int) -> Int {
var left = leftBoundary
var right = rightBoundary
while left <= right {
let mid = (left + right) / 2
if nums[mid] == target {
return mid
} else if nums[mid] > target {
right = mid - 1
} else {
left = mid + 1
}
}
return -1
}
// Binary search over elements on the pivot element's left
let answer = binarySearch(0, left - 1, target)
if answer != -1 {
return answer
}
// Binary search over elements on the pivot element's right
return binarySearch(left, n - 1, target)
}Please open Telegram to view this post
VIEW IN TELEGRAM
❤1
#medium
Задача: 34. Find First and Last Position of Element in Sorted Array
Дан массив целых чисел nums, отсортированный в неубывающем порядке, найдите начальную и конечную позицию заданного целевого значения.
Если целевое значение не найдено в массиве, верните [-1, -1].
Вы должны написать алгоритм со временной сложностью O(log n).
Пример:
👨💻 Алгоритм:
1️⃣ Определите функцию под названием findBound, которая принимает три аргумента: массив, целевое значение для поиска и булевое значение isFirst, указывающее, ищем ли мы первое или последнее вхождение цели.
Мы используем 2 переменные для отслеживания подмассива, который мы сканируем. Назовем их begin и end. Изначально begin устанавливается в 0, а end — в последний индекс массива.
2️⃣ Мы итерируем, пока begin не станет больше, чем end.
На каждом шаге мы вычисляем средний элемент mid = (begin + end) / 2. Мы используем значение среднего элемента, чтобы решить, какую половину массива нам нужно искать.
Если nums[mid] == target:
Если isFirst true — это означает, что мы пытаемся найти первое вхождение элемента. Если mid == begin или nums[mid - 1] != target, тогда мы возвращаем mid как первое вхождение цели. В противном случае мы обновляем end = mid - 1.
Если isFirst false — это означает, что мы пытаемся найти последнее вхождение элемента. Если mid == end или nums[mid + 1] != target, тогда мы возвращаем mid как последнее вхождение цели. В противном случае мы обновляем begin = mid + 1.
3️⃣ Если nums[mid] > target — мы обновляем end = mid - 1, так как мы должны отбросить правую сторону массива, поскольку средний элемент больше цели.
Если nums[mid] < target — мы обновляем begin = mid + 1, так как мы должны отбросить левую сторону массива, поскольку средний элемент меньше цели.
В конце нашей функции мы возвращаем значение -1, что указывает на то, что цель не найдена в массиве.
В основной функции searchRange мы сначала вызываем findBound с isFirst, установленным в true. Если это значение равно -1, мы можем просто вернуть [-1, -1]. В противном случае мы вызываем findBound с isFirst, установленным в false, чтобы получить последнее вхождение, а затем возвращаем результат.
😎 Решение:
🪙 823 вопроса вопроса на iOS разработчика
🔒 База собесов | 🔒 База тестовых
Задача: 34. Find First and Last Position of Element in Sorted Array
Дан массив целых чисел nums, отсортированный в неубывающем порядке, найдите начальную и конечную позицию заданного целевого значения.
Если целевое значение не найдено в массиве, верните [-1, -1].
Вы должны написать алгоритм со временной сложностью O(log n).
Пример:
Input: nums = [5,7,7,8,8,10], target = 8
Output: [3,4]
Мы используем 2 переменные для отслеживания подмассива, который мы сканируем. Назовем их begin и end. Изначально begin устанавливается в 0, а end — в последний индекс массива.
На каждом шаге мы вычисляем средний элемент mid = (begin + end) / 2. Мы используем значение среднего элемента, чтобы решить, какую половину массива нам нужно искать.
Если nums[mid] == target:
Если isFirst true — это означает, что мы пытаемся найти первое вхождение элемента. Если mid == begin или nums[mid - 1] != target, тогда мы возвращаем mid как первое вхождение цели. В противном случае мы обновляем end = mid - 1.
Если isFirst false — это означает, что мы пытаемся найти последнее вхождение элемента. Если mid == end или nums[mid + 1] != target, тогда мы возвращаем mid как последнее вхождение цели. В противном случае мы обновляем begin = mid + 1.
Если nums[mid] < target — мы обновляем begin = mid + 1, так как мы должны отбросить левую сторону массива, поскольку средний элемент меньше цели.
В конце нашей функции мы возвращаем значение -1, что указывает на то, что цель не найдена в массиве.
В основной функции searchRange мы сначала вызываем findBound с isFirst, установленным в true. Если это значение равно -1, мы можем просто вернуть [-1, -1]. В противном случае мы вызываем findBound с isFirst, установленным в false, чтобы получить последнее вхождение, а затем возвращаем результат.
func searchRange(_ nums: [Int], _ target: Int) -> [Int] {
let firstOccurrence = findBound(nums, target, true)
if firstOccurrence == -1 {
return [-1, -1]
}
let lastOccurrence = findBound(nums, target, false)
return [firstOccurrence, lastOccurrence]
}
func findBound(_ nums: [Int], _ target: Int, _ isFirst: Bool) -> Int {
let N = nums.count
var begin = 0
var end = N - 1
while begin <= end {
let mid = (begin + end) / 2
if nums[mid] == target {
if isFirst {
if mid == begin || nums[mid - 1] != target {
return mid
}
end = mid - 1
} else {
if mid == end || nums[mid + 1] != target {
return mid
}
begin = mid + 1
}
} else if nums[mid] > target {
end = mid - 1
} else {
begin = mid + 1
}
}
return -1
}Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1
#easy
Задача: 35. Search Insert Position
Дан отсортированный массив уникальных целых чисел и целевое значение. Верните индекс, если цель найдена. Если нет, верните индекс, где она должна быть вставлена в соответствии с порядком.
Вы должны написать алгоритм со временной сложностью O(log n).
Пример:
👨💻 Алгоритм:
1️⃣ Инициализируйте указатели left и right: left = 0, right = n - 1.
2️⃣ Пока left <= right:
Сравните средний элемент массива nums[pivot] с целевым значением target.
Если средний элемент является целевым, то есть target == nums[pivot]: верните pivot.
Если цель не найдена:
Если target < nums[pivot], продолжайте поиск в левом подмассиве. right = pivot - 1.
Иначе продолжайте поиск в правом подмассиве. left = pivot + 1.
3️⃣ Верните left.
😎 Решение:
🪙 823 вопроса вопроса на iOS разработчика
🔒 База собесов | 🔒 База тестовых
Задача: 35. Search Insert Position
Дан отсортированный массив уникальных целых чисел и целевое значение. Верните индекс, если цель найдена. Если нет, верните индекс, где она должна быть вставлена в соответствии с порядком.
Вы должны написать алгоритм со временной сложностью O(log n).
Пример:
Input: nums = [1,3,5,6], target = 5
Output: 2
Сравните средний элемент массива nums[pivot] с целевым значением target.
Если средний элемент является целевым, то есть target == nums[pivot]: верните pivot.
Если цель не найдена:
Если target < nums[pivot], продолжайте поиск в левом подмассиве. right = pivot - 1.
Иначе продолжайте поиск в правом подмассиве. left = pivot + 1.
class Solution {
func searchInsert(_ nums: [Int], _ target: Int) -> Int {
var left = 0
var right = nums.count - 1
while left <= right {
let pivot = left + (right - left) / 2
if nums[pivot] == target {
return pivot
}
if target < nums[pivot] {
right = pivot - 1
} else {
left = pivot + 1
}
}
return left
}
}Please open Telegram to view this post
VIEW IN TELEGRAM
❤1
#medium
Задача: 36. Valid Sudoku
Определите, является ли доска Судоку размером 9 на 9 валидной. Необходимо проверить только заполненные ячейки согласно следующим правилам:
Каждая строка должна содержать цифры от 1 до 9 без повторений.
Каждый столбец должен содержать цифры от 1 до 9 без повторений.
Каждая из девяти подзон размером 3 на 3 в сетке должна содержать цифры от 1 до 9 без повторений.
Пример:
👨💻 Алгоритм:
1️⃣ Инициализируйте список из 9 хеш-множеств, где хеш-множество с индексом r будет использоваться для хранения ранее увиденных чисел в строке r судоку. Аналогично инициализируйте списки из 9 хеш-множеств для отслеживания столбцов и блоков.
2️⃣ Итерируйтесь по каждой позиции (r, c) в судоку. На каждой итерации, если на текущей позиции есть число:
Проверьте, существует ли это число в хеш-множестве для текущей строки, столбца или блока. Если да, верните false, потому что это второе появление числа в текущей строке, столбце или блоке.
3️⃣ В противном случае обновите множество, отвечающее за отслеживание ранее увиденных чисел в текущей строке, столбце и блоке. Индекс текущего блока рассчитывается как (r / 3) * 3 + (c / 3), где / означает деление нацело.
Если дубликаты не были найдены после посещения каждой позиции на доске судоку, то судоку валидно, поэтому верните true.
😎 Решение:
🪙 823 вопроса вопроса на iOS разработчика
🔒 База собесов | 🔒 База тестовых
Задача: 36. Valid Sudoku
Определите, является ли доска Судоку размером 9 на 9 валидной. Необходимо проверить только заполненные ячейки согласно следующим правилам:
Каждая строка должна содержать цифры от 1 до 9 без повторений.
Каждый столбец должен содержать цифры от 1 до 9 без повторений.
Каждая из девяти подзон размером 3 на 3 в сетке должна содержать цифры от 1 до 9 без повторений.
Пример:
Input: board =
[["5","3",".",".","7",".",".",".","."]
,["6",".",".","1","9","5",".",".","."]
,[".","9","8",".",".",".",".","6","."]
,["8",".",".",".","6",".",".",".","3"]
,["4",".",".","8",".","3",".",".","1"]
,["7",".",".",".","2",".",".",".","6"]
,[".","6",".",".",".",".","2","8","."]
,[".",".",".","4","1","9",".",".","5"]
,[".",".",".",".","8",".",".","7","9"]]
Output: true
Проверьте, существует ли это число в хеш-множестве для текущей строки, столбца или блока. Если да, верните false, потому что это второе появление числа в текущей строке, столбце или блоке.
Если дубликаты не были найдены после посещения каждой позиции на доске судоку, то судоку валидно, поэтому верните true.
func isValidSudoku(_ board: [[Character]]) -> Bool {
let N = 9
var rows = Array(repeating: [Character: Bool](), count: N)
var cols = Array(repeating: [Character: Bool](), count: N)
var boxes = Array(repeating: [Character: Bool](), count: N)
for r in 0..<N {
for c in 0..<N {
let val = board[r][c]
if val == "." {
continue
}
if rows[r][val] != nil {
return false
}
rows[r][val] = true
if cols[c][val] != nil {
return false
}
cols[c][val] = true
let idx = (r / 3) * 3 + (c / 3)
if boxes[idx][val] != nil {
return false
}
boxes[idx][val] = true
}
}
return true
}Please open Telegram to view this post
VIEW IN TELEGRAM
❤1
#hard
Задача: 37. Sudoku Solver
Напишите программу для решения головоломки Судоку, заполнив пустые ячейки.
Решение Судоку должно удовлетворять всем следующим правилам:
Каждая из цифр от 1 до 9 должна встречаться ровно один раз в каждой строке.
Каждая из цифр от 1 до 9 должна встречаться ровно один раз в каждом столбце.
Каждая из цифр от 1 до 9 должна встречаться ровно один раз в каждом из 9 подблоков 3x3 сетки.
Символ '.' обозначает пустые ячейки.
Пример:
👨💻 Алгоритм:
1️⃣ Теперь все готово для написания функции обратного поиска backtrack(row = 0, col = 0). Начните с верхней левой ячейки row = 0, col = 0. Продолжайте, пока не дойдете до первой свободной ячейки.
2️⃣ Итерируйте по числам от 1 до 9 и попробуйте поставить каждое число d в ячейку (row, col).
Если число d еще не в текущей строке, столбце и блоке:
Поместите d в ячейку (row, col).
Запишите, что d теперь присутствует в текущей строке, столбце и блоке.
3️⃣ Если вы на последней ячейке row == 8, col == 8:
Это означает, что судоку решено.
В противном случае продолжайте размещать дальнейшие числа.
Откат, если решение еще не найдено: удалите последнее число из ячейки (row, col).
😎 Решение:
🪙 823 вопроса вопроса на iOS разработчика
🔒 База собесов | 🔒 База тестовых
Задача: 37. Sudoku Solver
Напишите программу для решения головоломки Судоку, заполнив пустые ячейки.
Решение Судоку должно удовлетворять всем следующим правилам:
Каждая из цифр от 1 до 9 должна встречаться ровно один раз в каждой строке.
Каждая из цифр от 1 до 9 должна встречаться ровно один раз в каждом столбце.
Каждая из цифр от 1 до 9 должна встречаться ровно один раз в каждом из 9 подблоков 3x3 сетки.
Символ '.' обозначает пустые ячейки.
Пример:
Input: board =
[["5","3",".",".","7",".",".",".","."],
["6",".",".","1","9","5",".",".","."],
[".","9","8",".",".",".",".","6","."],
["8",".",".",".","6",".",".",".","3"],
["4",".",".","8",".","3",".",".","1"],
["7",".",".",".","2",".",".",".","6"],
[".","6",".",".",".",".","2","8","."],
[".",".",".","4","1","9",".",".","5"],
[".",".",".",".","8",".",".","7","9"]]
Output:
[["5","3","4","6","7","8","9","1","2"],["6","7","2","1","9","5","3","4","8"],["1","9","8","3","4","2","5","6","7"],["8","5","9","7","6","1","4","2","3"],["4","2","6","8","5","3","7","9","1"],["7","1","3","9","2","4","8","5","6"],["9","6","1","5","3","7","2","8","4"],["2","8","7","4","1","9","6","3","5"],["3","4","5","2","8","6","1","7","9"]]
Если число d еще не в текущей строке, столбце и блоке:
Поместите d в ячейку (row, col).
Запишите, что d теперь присутствует в текущей строке, столбце и блоке.
Это означает, что судоку решено.
В противном случае продолжайте размещать дальнейшие числа.
Откат, если решение еще не найдено: удалите последнее число из ячейки (row, col).
import Foundation
class Solution {
func solveSudoku(_ board: inout [[Character]]) {
let n = 3
let N = n * n
var tracking = Array(repeating: [Character: Int](), count: N * 3)
func index(_ type: Int, _ i: Int) -> Int { type * N + i }
func boxIndex(_ r: Int, _ c: Int) -> Int { (r / n) * n + c / n }
func canPlace(_ num: Character, _ r: Int, _ c: Int) -> Bool {
let bIdx = boxIndex(r, c)
return tracking[index(0, r)][num] == nil &&
tracking[index(1, c)][num] == nil &&
tracking[index(2, bIdx)][num] == nil
}
func placeOrRemove(_ num: Character, _ r: Int, _ c: Int, place: Bool) {
let bIdx = boxIndex(r, c)
let adjustment = place ? 1 : -1
tracking[index(0, r)][num, default: 0] += adjustment
tracking[index(1, c)][num, default: 0] += adjustment
tracking[index(2, bIdx)][num, default: 0] += adjustment
board[r][c] = place ? num : "."
}
func backtrack(_ r: Int = 0, _ c: Int = 0) {
if c == N && r == N - 1 { return }
let nextR = c == N - 1 ? r + 1 : r
let nextC = c == N - 1 ? 0 : c + 1
if board[r][c] == "." {
for num in "123456789" {
if canPlace(num, r, c) {
placeOrRemove(num, r, c, place: true)
backtrack(nextR, nextC)
placeOrRemove(num, r, c, place: false)
}
}
} else {
backtrack(nextR, nextC)
}
}
for r in 0..<N {
for c in 0..<N where board[r][c] != "." {
placeOrRemove(board[r][c], r, c, place: true)
}
}
backtrack()
}
}
Please open Telegram to view this post
VIEW IN TELEGRAM
❤1
#medium
Задача: 38. Count and Say
Последовательность "считай и скажи" — это последовательность строк цифр, определяемая с помощью рекурсивной формулы:
countAndSay(1) = "1"
countAndSay(n) — это кодирование длин серий из countAndSay(n - 1).
Кодирование длин серий (RLE) — это метод сжатия строк, который работает путём замены последовательных идентичных символов (повторяющихся 2 или более раз) на конкатенацию символа и числа, обозначающего количество символов (длину серии). Например, чтобы сжать строку "3322251", мы заменяем "33" на "23", "222" на "32", "5" на "15" и "1" на "11". Таким образом, сжатая строка становится "23321511".
Для заданного положительного целого числа n верните n-й элемент последовательности "считай и скажи".
Пример:
👨💻 Алгоритм:
1️⃣ Мы хотим использовать шаблон, который соответствует строкам из одинаковых символов, таких как "4", "7777", "2222222".
Если у вас есть опыт работы с регулярными выражениями, вы можете обнаружить, что шаблон (.)\1* работает.
2️⃣ Мы можем разбить это регулярное выражение на три части:
(.): оно определяет группу, содержащую один символ, который может быть чем угодно.
3️⃣ *: этот квалификатор, следующий за ссылкой на группу \1, указывает, что мы хотели бы видеть повторение группы ноль или более раз.
Таким образом, шаблон соответствует строкам, которые состоят из некоторого символа, а затем ноль или более повторений этого символа после его первого появления. Это то, что нам нужно.
Мы находим все совпадения с регулярным выражением, а затем конкатенируем результаты.
😎 Решение:
🪙 823 вопроса вопроса на iOS разработчика
🔒 База собесов | 🔒 База тестовых
Задача: 38. Count and Say
Последовательность "считай и скажи" — это последовательность строк цифр, определяемая с помощью рекурсивной формулы:
countAndSay(1) = "1"
countAndSay(n) — это кодирование длин серий из countAndSay(n - 1).
Кодирование длин серий (RLE) — это метод сжатия строк, который работает путём замены последовательных идентичных символов (повторяющихся 2 или более раз) на конкатенацию символа и числа, обозначающего количество символов (длину серии). Например, чтобы сжать строку "3322251", мы заменяем "33" на "23", "222" на "32", "5" на "15" и "1" на "11". Таким образом, сжатая строка становится "23321511".
Для заданного положительного целого числа n верните n-й элемент последовательности "считай и скажи".
Пример:
Input: n = 4
Output: "1211"
Explanation:
countAndSay(1) = "1"
countAndSay(2) = RLE of "1" = "11"
countAndSay(3) = RLE of "11" = "21"
countAndSay(4) = RLE of "21" = "1211"
Если у вас есть опыт работы с регулярными выражениями, вы можете обнаружить, что шаблон (.)\1* работает.
(.): оно определяет группу, содержащую один символ, который может быть чем угодно.
Таким образом, шаблон соответствует строкам, которые состоят из некоторого символа, а затем ноль или более повторений этого символа после его первого появления. Это то, что нам нужно.
Мы находим все совпадения с регулярным выражением, а затем конкатенируем результаты.
func countAndSay(_ n: Int) -> String {
var s = "1"
for _ in 2...n {
var t = ""
var matches = Array(s.matchingStrings(regex: "(.)\\1*"))
for match in matches {
t += "\(match[0].count)\(match[1])"
}
s = t
}
return s
}
extension String {
func matchingStrings(regex: String) -> [[String]] {
do {
let regex = try NSRegularExpression(pattern: regex)
let results = regex.matches(in: self, range: NSRange(self.startIndex..., in: self))
return results.map { result in
(0..<result.numberOfRanges).map {
String(self[Range(result.range(at: $0), in: self)!])
}
}
} catch let error {
print("invalid regex: \(error.localizedDescription)")
return []
}
}
}Please open Telegram to view this post
VIEW IN TELEGRAM
❤1
#medium
Задача: 39. Combination Sum
Дан массив уникальных целых чисел candidates и целевое целое число target. Верните список всех уникальных комбинаций из candidates, где выбранные числа в сумме дают target. Комбинации можно возвращать в любом порядке.
Одно и то же число может быть выбрано из массива candidates неограниченное количество раз. Две комбинации считаются уникальными, если частота хотя бы одного из выбранных чисел отличается.
Тестовые случаи сгенерированы таким образом, что количество уникальных комбинаций, дающих в сумме target, меньше 150 комбинаций для данного ввода.
Пример:
👨💻 Алгоритм:
1️⃣ Как видно, вышеописанный алгоритм обратного отслеживания разворачивается как обход дерева в глубину (DFS - Depth-First Search), который часто реализуется с помощью рекурсии.
Здесь мы определяем рекурсивную функцию backtrack(remain, comb, start) (на Python), которая заполняет комбинации, начиная с текущей комбинации (comb), оставшейся суммы для выполнения (remain) и текущего курсора (start) в списке кандидатов.
Следует отметить, что сигнатура рекурсивной функции немного отличается в Java, но идея остается той же.
2️⃣ Для первого базового случая рекурсивной функции, если remain == 0, то есть мы достигаем желаемой целевой суммы, поэтому мы можем добавить текущую комбинацию в итоговый список.
Как другой базовый случай, если remain < 0, то есть мы превышаем целевое значение, мы прекращаем исследование на этом этапе.
3️⃣ Помимо вышеупомянутых двух базовых случаев, мы затем продолжаем исследовать подсписок кандидатов, начиная с [start ... n].
Для каждого из кандидатов мы вызываем рекурсивную функцию саму с обновленными параметрами.
Конкретно, мы добавляем текущего кандидата в комбинацию.
С добавленным кандидатом у нас теперь меньше суммы для выполнения, то есть remain - candidate.
Для следующего исследования мы все еще начинаем с текущего курсора start.
В конце каждого исследования мы делаем откат, удаляя кандидата из комбинации.
😎 Решение:
🔥 ТОП ВОПРОСОВ С СОБЕСОВ
🔒 База собесов | 🔒 База тестовых
Задача: 39. Combination Sum
Дан массив уникальных целых чисел candidates и целевое целое число target. Верните список всех уникальных комбинаций из candidates, где выбранные числа в сумме дают target. Комбинации можно возвращать в любом порядке.
Одно и то же число может быть выбрано из массива candidates неограниченное количество раз. Две комбинации считаются уникальными, если частота хотя бы одного из выбранных чисел отличается.
Тестовые случаи сгенерированы таким образом, что количество уникальных комбинаций, дающих в сумме target, меньше 150 комбинаций для данного ввода.
Пример:
Input: candidates = [2,3,5], target = 8
Output: [[2,2,2,2],[2,3,3],[3,5]]
Здесь мы определяем рекурсивную функцию backtrack(remain, comb, start) (на Python), которая заполняет комбинации, начиная с текущей комбинации (comb), оставшейся суммы для выполнения (remain) и текущего курсора (start) в списке кандидатов.
Следует отметить, что сигнатура рекурсивной функции немного отличается в Java, но идея остается той же.
Как другой базовый случай, если remain < 0, то есть мы превышаем целевое значение, мы прекращаем исследование на этом этапе.
Для каждого из кандидатов мы вызываем рекурсивную функцию саму с обновленными параметрами.
Конкретно, мы добавляем текущего кандидата в комбинацию.
С добавленным кандидатом у нас теперь меньше суммы для выполнения, то есть remain - candidate.
Для следующего исследования мы все еще начинаем с текущего курсора start.
В конце каждого исследования мы делаем откат, удаляя кандидата из комбинации.
class Solution {
func backtrack(_ remain: Int, _ comb: inout [Int], _ start: Int, _ candidates: [Int], _ results: inout [[Int]]) {
if remain == 0 {
results.append(comb)
return
} else if remain < 0 {
return
}
for i in start..<candidates.count {
comb.append(candidates[i])
backtrack(remain - candidates[i], &comb, i, candidates, &results)
comb.removeLast()
}
}
func combinationSum(_ candidates: [Int], _ target: Int) -> [[Int]] {
var results = [[Int]]()
var comb = [Int]()
backtrack(target, &comb, 0, candidates, &results)
return results
}
}Please open Telegram to view this post
VIEW IN TELEGRAM
❤1
#medium
Задача: 40. Combination Sum II
Дана коллекция кандидатов (candidates) и целевое число (target). Найдите все уникальные комбинации в candidates, где числа кандидатов в сумме дают target.
Каждое число в candidates может быть использовано только один раз в комбинации.
Примечание: Набор решений не должен содержать повторяющихся комбинаций.
Пример:
👨💻 Алгоритм:
1️⃣ Во-первых, мы создаём таблицу счётчиков из предоставленного списка чисел. Затем мы используем эту таблицу счётчиков в процессе обратного поиска, который мы определяем как функцию
2️⃣ При каждом вызове функции обратного поиска мы сначала проверяем, достигли ли мы целевой суммы (то есть
3️⃣ Если осталась сумма для заполнения, мы затем перебираем текущую таблицу счётчиков, чтобы выбрать следующего кандидата. После выбора кандидата мы продолжаем исследование, вызывая функцию
😎 Решение:
🔥 ТОП ВОПРОСОВ С СОБЕСОВ
🔒 База собесов | 🔒 База тестовых
Задача: 40. Combination Sum II
Дана коллекция кандидатов (candidates) и целевое число (target). Найдите все уникальные комбинации в candidates, где числа кандидатов в сумме дают target.
Каждое число в candidates может быть использовано только один раз в комбинации.
Примечание: Набор решений не должен содержать повторяющихся комбинаций.
Пример:
Input: candidates = [10,1,2,7,6,1,5], target = 8
Output:
[
[1,1,6],
[1,2,5],
[1,7],
[2,6]
]
backtrack(comb, remain, curr, candidate_groups, results). Для сохранения состояния на каждом этапе обратного поиска мы используем несколько параметров в функции:comb: комбинация, которую мы построили на данный момент.remain: оставшаяся сумма, которую нам нужно заполнить, чтобы достичь целевой суммы.curr: курсор, который указывает на текущую группу чисел, используемую из таблицы счётчиков.counter: текущая таблица счётчиков.results: окончательные комбинации, которые достигают целевой суммы.sum(comb) = target), и нужно ли прекратить исследование, потому что сумма текущей комбинации превышает желаемую целевую сумму.backtrack() с обновлёнными состояниями. Более важно, что в конце каждого исследования нам нужно вернуть состояние, которое мы обновили ранее, чтобы начать с чистого листа для следующего исследования. Именно из-за этой операции обратного поиска алгоритм получил своё название.class Solution {
func combinationSum2(_ candidates: [Int], _ target: Int) -> [[Int]] {
let candidates = candidates.sorted()
var res = [[Int]]()
findCombinationSum2(candidates, target, 0, [], &res)
return res
}
private func findCombinationSum2(_ candidates: [Int], _ target: Int, _ start: Int, _ current: [Int], _ res: inout [[Int]]) {
if target == 0 {
res.append(current)
return
}
var current = current
for i in start..<candidates.count {
if i > start && candidates[i] == candidates[i - 1] {
continue
}
if candidates[i] > target {
break
}
current.append(candidates[i])
findCombinationSum2(candidates, target - candidates[i], i + 1, current, &res)
current.removeLast()
}
}
}Please open Telegram to view this post
VIEW IN TELEGRAM
❤1