Golang | LeetCode
3.9K subscribers
172 photos
1.05K links
Cайт easyoffer.ru
Реклама @easyoffer_adv
ВП @easyoffer_vp

Тесты t.iss.one/+MVqzqT6ZzFFhYjhi
Вопросы собесов t.iss.one/+ajHN0OKU1okyZDky
Вакансии t.iss.one/+mX_RBWjiMTExODUy
Download Telegram
Задача: 748. Shortest Completing Word
Сложность: easy

Вам дан целочисленный массив nums, в котором наибольшее целое число уникально. Определите, является ли наибольший элемент массива по крайней мере в два раза больше всех остальных чисел в массиве. Если да, то верните индекс самого большого элемента, в противном случае верните -1.

Пример:
Input: licensePlate = "1s3 PSt", words = ["step","steps","stripe","stepple"]
Output: "steps"


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

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

2⃣Пройти по массиву words, проверяя каждое слово на соответствие требованиям.

3⃣Найти самое короткое завершающее слово среди подходящих.

😎 Решение:
package main

func shortestCompletingWord(licensePlate string, words []string) string {
licenseCount := getCharCount(licensePlate)

var result string
for _, word := range words {
if isCompletingWord(word, licenseCount) {
if result == "" || len(word) < len(result) {
result = word
}
}
}
return result
}

func getCharCount(s string) map[rune]int {
count := make(map[rune]int)
for _, c := range s {
if isLetter(c) {
count[toLower(c)]++
}
}
return count
}

func isCompletingWord(word string, licenseCount map[rune]int) bool {
wordCount := make(map[rune]int)
for _, c := range word {
wordCount[c]++
}
for char, cnt := range licenseCount {
if wordCount[char] < cnt {
return false
}
}
return true
}

func isLetter(c rune) bool {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
}

func toLower(c rune) rune {
if c >= 'A' && c <= 'Z' {
return c + 'a' - 'A'
}
return c
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 288. Unique Word Abbreviation
Сложность: medium

Сокращение слова — это объединение его первой буквы, количества символов между первой и последней буквой и последней буквы. Если слово состоит только из двух символов, то оно является сокращением само по себе.

Например:
dog --> d1g, потому что между первой буквой 'd' и последней буквой 'g' одна буква.
internationalization --> i18n, потому что между первой буквой 'i' и последней буквой 'n' 18 букв.
it --> it, потому что любое слово из двух символов является своим собственным сокращением.
Реализуйте класс ValidWordAbbr:

ValidWordAbbr(String[] dictionary) Инициализирует объект со словарем слов.
boolean isUnique(string word) Возвращает true, если выполняется одно из следующих условий (в противном случае возвращает false):
В словаре нет слова, сокращение которого равно сокращению слова word.
Для любого слова в словаре, сокращение которого равно сокращению слова word, это слово и word одинаковы.

Пример:
Input
["ValidWordAbbr", "isUnique", "isUnique", "isUnique", "isUnique", "isUnique"]
[[["deer", "door", "cake", "card"]], ["dear"], ["cart"], ["cane"], ["make"], ["cake"]]
Output
[null, false, true, false, true, true]

Explanation
ValidWordAbbr validWordAbbr = new ValidWordAbbr(["deer", "door", "cake", "card"]);
validWordAbbr.isUnique("dear"); // return false, dictionary word "deer" and word "dear" have the same abbreviation "d2r" but are not the same.
validWordAbbr.isUnique("cart"); // return true, no words in the dictionary have the abbreviation "c2t".
validWordAbbr.isUnique("cane"); // return false, dictionary word "cake" and word "cane" have the same abbreviation "c2e" but are not the same.
validWordAbbr.isUnique("make"); // return true, no words in the dictionary have the abbreviation "m2e".
validWordAbbr.isUnique("cake"); // return true, because "cake" is already in the dictionary and no other word in the dictionary has "c2e" abbreviation.


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

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

2⃣Генерация сокращений:
При инициализации объекта ValidWordAbbr пройдите через каждое слово в словаре и создайте его сокращение.
Если сокращение уже существует в abbrDict, установите значение в false (не уникальное). В противном случае установите значение в true (уникальное).

3⃣Проверка уникальности:
Для метода isUnique создайте сокращение для входного слова и проверьте, есть ли это сокращение в abbrDict.
Если сокращение отсутствует в abbrDict, возвращайте true.
Если сокращение присутствует и оно уникально, проверьте, есть ли это слово в словаре. Если да, возвращайте true, в противном случае - false.

😎 Решение:
type ValidWordAbbr struct {
abbrDict map[string]bool
dict map[string]struct{}
}

func Constructor(dictionary []string) ValidWordAbbr {
abbrDict := make(map[string]bool)
dict := make(map[string]struct{})
for _, word := range dictionary {
dict[word] = struct{}{}
abbr := toAbbr(word)
abbrDict[abbr] = !abbrDict[abbr]
}
return ValidWordAbbr{abbrDict: abbrDict, dict: dict}
}

func (this *ValidWordAbbr) IsUnique(word string) bool {
abbr := toAbbr(word)
_, hasAbbr := this.abbrDict[abbr]
_, inDict := this.dict[word]
return !hasAbbr || (this.abbrDict[abbr] && inDict)
}

func toAbbr(word string) string {
n := len(word)
if n <= 2 {
return word
}
return fmt.Sprintf("%c%d%c", word[0], n-2, word[n-1])
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 137. Single Number II
Сложность: medium

Дан массив целых чисел nums, в котором каждый элемент встречается три раза, кроме одного, который встречается ровно один раз. Найдите этот единственный элемент и верните его.

Вы должны реализовать решение с линейной сложностью выполнения и использовать только постоянное дополнительное пространство.

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


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

1⃣Сортировка массива:
Отсортируйте массив nums. Это упорядочит все элементы так, чтобы одинаковые числа находились рядом.

2⃣Итерация с проверкой:
Используйте цикл for для перебора элементов массива от начала до nums.size() - 2 с шагом 3. Таким образом, каждый проверяемый индекс будет иметь следующий за ним индекс в пределах массива.
Если элемент на текущем индексе совпадает с элементом на следующем индексе (проверка nums[i] == nums[i + 1]), продолжайте следующую итерацию цикла.

3⃣Возврат уникального элемента:
Если элемент на текущем индексе не совпадает с следующим, значит, это искомый уникальный элемент, который встречается только один раз. В этом случае возвращайте элемент на текущем индексе.
Если до последнего элемента цикл не нашёл уникального элемента, возвращайте последний элемент массива nums[nums.size() - 1], поскольку он, очевидно, будет уникальным, если предыдущие проверки не выявили уникального элемента раньше.

😎 Решение:
func singleNumber(nums []int) int {
sort.Ints(nums)
for i := 0; i < len(nums)-1; i += 3 {
if nums[i] == nums[i+1] {
continue
} else {
return nums[i]
}
}
return nums[len(nums)-1]
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 173. Binary Search Tree Iterator
Сложность: medium

Реализуйте класс BSTIterator, который представляет итератор по обходу бинарного дерева поиска (BST) в порядке in-order:

BSTIterator(TreeNode root): Инициализирует объект класса BSTIterator. Корень BST передается в качестве параметра конструктора. Указатель должен быть инициализирован на несуществующее число, меньшее любого элемента в BST.
boolean hasNext(): Возвращает true, если в обходе справа от указателя существует число, иначе возвращает false.
int next(): Перемещает указатель вправо, затем возвращает число на указателе.
Обратите внимание, что при инициализации указателя на несуществующее наименьшее число, первый вызов next() вернет наименьший элемент в BST.
Можно предположить, что вызовы next() всегда будут допустимы. То есть, при вызове next() в обходе всегда будет хотя бы одно следующее число.

Пример:
Input
["BSTIterator", "next", "next", "hasNext", "next", "hasNext", "next", "hasNext", "next", "hasNext"]
[[[7, 3, 15, null, null, 9, 20]], [], [], [], [], [], [], [], [], []]
Output
[null, 3, 7, true, 9, true, 15, true, 20, false]

Explanation
BSTIterator bSTIterator = new BSTIterator([7, 3, 15, null, null, 9, 20]);
bSTIterator.next(); // return 3
bSTIterator.next(); // return 7
bSTIterator.hasNext(); // return True
bSTIterator.next(); // return 9
bSTIterator.hasNext(); // return True
bSTIterator.next(); // return 15
bSTIterator.hasNext(); // return True
bSTIterator.next(); // return 20
bSTIterator.hasNext(); // return False


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

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

2⃣Мы обходим бинарное дерево поиска в порядке in-order и для каждого узла, который обрабатываем, добавляем его в наш массив узлов. Обратите внимание, что перед обработкой узла сначала нужно обработать (или рекурсивно вызвать) его левое поддерево, а после обработки узла — его правое поддерево.

3⃣Когда у нас будут все узлы в массиве, нам просто нужен указатель или индекс в этом массиве для реализации двух функций next и hasNext. Всякий раз, когда вызывается hasNext, мы просто проверяем, достиг ли индекс конца массива или нет. При вызове функции next мы просто возвращаем элемент, на который указывает индекс. Также, после вызова функции next, мы должны переместить индекс на один шаг вперед, чтобы имитировать прогресс нашего итератора.

😎 Решение:
package main

import "fmt"

type TreeNode struct {
Val int
Left *TreeNode
Right *TreeNode
}

type BSTIterator struct {
nodesSorted []int
index int
}

func Constructor(root *TreeNode) BSTIterator {
iterator := BSTIterator{nodesSorted: []int{}, index: -1}
iterator.inorder(root)
return iterator
}

func (this *BSTIterator) inorder(root *TreeNode) {
if root == nil {
return
}
this.inorder(root.Left)
this.nodesSorted = append(this.nodesSorted, root.Val)
this.inorder(root.Right)
}

func (this *BSTIterator) Next() int {
this.index++
return this.nodesSorted[this.index]
}

func (this *BSTIterator) HasNext() bool {
return this.index+1 < len(this.nodesSorted)
}

func main() {
root := &TreeNode{7,
&TreeNode{3, nil, nil},
&TreeNode{15,
&TreeNode{9, nil, nil},
&TreeNode{20, nil, nil}}}
iterator := Constructor(root)
fmt.Println(iterator.Next())
fmt.Println(iterator.HasNext())
fmt.Println(iterator.Next())
fmt.Println(iterator.HasNext())
fmt.Println(iterator.Next())
fmt.Println(iterator.HasNext())
fmt.Println(iterator.Next())
fmt.Println(iterator.HasNext())
fmt.Println(iterator.Next())
fmt.Println(iterator.HasNext())
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 928. Minimize Malware Spread II
Сложность: hard

Вам дана сеть из n узлов, представленная в виде графа с матрицей смежности n x n, где i-й узел непосредственно связан с j-м узлом, если graph[i][j] == 1. Некоторые узлы изначально заражены вредоносным ПО. Если два узла соединены напрямую и хотя бы один из них заражен вредоносным ПО, то оба узла будут заражены вредоносным ПО. Такое распространение вредоносного ПО будет продолжаться до тех пор, пока больше не останется ни одного узла, зараженного таким образом. Предположим, что M(initial) - это конечное число узлов, зараженных вредоносным ПО, во всей сети после прекращения распространения вредоносного ПО. Мы удалим ровно один узел из initial, полностью удалив его и все связи от этого узла к любому другому узлу. Верните узел, который, если его удалить, минимизирует M(initial). Если для минимизации M(initial) можно удалить несколько узлов, верните такой узел с наименьшим индексом.

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


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

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

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

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

😎 Решение:
package main

func minMalwareSpread(graph [][]int, initial []int) int {
n := len(graph)
visited := make(map[int]struct{})
components := []map[int]struct{}{}

var dfs func(int)
dfs = func(node int) {
stack := []int{node}
component := make(map[int]struct{})
for len(stack) > 0 {
u := stack[len(stack)-1]
stack = stack[:len(stack)-1]
if _, ok := visited[u]; !ok {
visited[u] = struct{}{}
component[u] = struct{}{}
for v := 0; v < n; v++ {
if graph[u][v] == 1 {
if _, ok := visited[v]; !ok {
stack = append(stack, v)
}
}
}
}
}
components = append(components, component)
}

for i := 0; i < n; i++ {
if _, ok := visited[i]; !ok {
dfs(i)
}
}

infectedInComponent := make([]int, len(components))
nodeToComponent := make([]int, n)

for i := range nodeToComponent {
nodeToComponent[i] = -1
}

for idx, component := range components {
for node := range component {
nodeToComponent[node] = idx
for _, init := range initial {
if node == init {
infectedInComponent[idx]++
}
}
}
}

minInfected := int(^uint(0) >> 1)
resultNode := initial[0]

for _, node := range initial {
componentIdx := nodeToComponent[node]
if infectedInComponent[componentIdx] == 1 {
componentSize := len(components[componentIdx])
if componentSize < minInfected || (componentSize == minInfected && node < resultNode) {
minInfected = componentSize
resultNode = node
}
}
}

return resultNode
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 1071. Greatest Common Divisor of Strings
Сложность: easy

Для двух строк s и t мы говорим, что "t делит s", если и только если s = t + t + t + ... + t (т.е. t соединена сама с собой один или более раз).

Даны две строки str1 и str2, верните наибольшую строку x, такую что x делит и str1, и str2.

Пример:
Input: str1 = "ABCABC", str2 = "ABC"
Output: "ABC"


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

1⃣Найдите более короткую строку среди str1 и str2, для простоты пусть это будет str1.

2⃣Начните с base = str1 и проверьте, состоят ли обе строки str1 и str2 из множителей строки base. Если это так, верните base. В противном случае, попробуйте более короткую строку, удалив последний символ из base.

3⃣Если вы проверили все префиксные строки и не нашли строку GCD, верните "".

😎 Решение:
func valid(str1, str2 string, k int) bool {
len1, len2 := len(str1), len(str2)
if len1 % k > 0 || len2 % k > 0 {
return false
} else {
base := str1[:k]
n1, n2 := len1 / k, len2 / k
return str1 == joinWords(base, n1) && str2 == joinWords(base, n2)
}
}

func joinWords(str string, k int) string {
ans := ""
for i := 0; i < k; i++ {
ans += str
}
return ans
}

func gcdOfStrings(str1, str2 string) string {
len1, len2 := len(str1), len(str2)
for i := min(len1, len2); i >= 1; i-- {
if valid(str1, str2, i) {
return str1[:i]
}
}
return ""
}

func min(a, b int) int {
if a < b {
return a
}
return b
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 530. Minimum Absolute Difference in BST
Сложность: easy

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

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


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

1⃣ Обход дерева в порядке возрастания (инфиксный обход):
Создайте список inorderNodes для хранения значений узлов.
Выполните инфиксный обход дерева, добавляя значения узлов в список.

2⃣ Нахождение минимальной разницы:
Создайте переменную minDifference и инициализируйте её бесконечностью.
Пройдите по списку inorderNodes, начиная с индекса 1, и найдите минимальную абсолютную разницу между последовательными элементами.

3⃣ Возврат результата:
Верните minDifference.

😎 Решение:
import (
"math"
)

type TreeNode struct {
Val int
Left *TreeNode
Right *TreeNode
}

type Solution struct {
inorderNodes []int
}

func (s *Solution) getMinimumDifference(root *TreeNode) int {
s.inorderNodes = []int{}
s.inorderTraversal(root)
minDifference := math.MaxInt32
for i := 1; i < len(s.inorderNodes); i++ {
minDifference = min(minDifference, s.inorderNodes[i]-s.inorderNodes[i-1])
}
return minDifference
}

func (s *Solution) inorderTraversal(node *TreeNode) {
if node == nil {
return
}
s.inorderTraversal(node.Left)
s.inorderNodes = append(s.inorderNodes, node.Val)
s.inorderTraversal(node.Right)
}

func min(a, b int) int {
if a < b {
return a
}
return b
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 1056. Confusing Number
Сложность: easy

Запутанное число - это число, которое при повороте на 180 градусов становится другим числом, каждая цифра которого действительна. Мы можем повернуть цифры числа на 180 градусов, чтобы получить новые цифры. Когда 0, 1, 6, 8 и 9 поворачиваются на 180 градусов, они становятся 0, 1, 9, 8 и 6 соответственно.
При повороте на 180 градусов 2, 3, 4, 5 и 7 становятся недействительными. Обратите внимание, что после поворота числа мы можем игнорировать ведущие нули. Например, после поворота 8000 мы получим 0008, которое считается просто 8. Если задано целое число n, верните true, если это запутанное число, или false в противном случае.

Пример:
Input: n = 6
Output: true


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

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

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

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

😎 Решение:
package main

import (
"strconv"
)

func isConfusingNumber(n int) bool {
rotationMap := map[rune]rune{'0': '0', '1': '1', '6': '9', '8': '8', '9': '6'}
nStr := strconv.Itoa(n)
rotatedStr := ""

for _, ch := range nStr {
if _, exists := rotationMap[ch]; !exists {
return false
}
rotatedStr = string(rotationMap[ch]) + rotatedStr
}

return rotatedStr != nStr
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 1522. Diameter of N-Ary Tree
Сложность: medium

Дано N-арное дерево, нужно найти диаметр — длину самого длинного пути между двумя узлами дерева (не обязательно проходящего через корень).

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


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

1⃣Рекурсивная функция высоты
Реализуем height(node) — возвращает максимальную высоту поддерева. Для листа это 0. Для внутреннего узла — 1 + макс(высота дочернего).

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

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

😎 Решение:
type Node struct {
Val int
Children []*Node
}

type Solution struct {
diameter int
}

func (s *Solution) height(node *Node) int {
if len(node.Children) == 0 {
return 0
}

maxHeight1, maxHeight2 := 0, 0

for _, child := range node.Children {
h := s.height(child) + 1
if h > maxHeight1 {
maxHeight2 = maxHeight1
maxHeight1 = h
} else if h > maxHeight2 {
maxHeight2 = h
}

if maxHeight1+maxHeight2 > s.diameter {
s.diameter = maxHeight1 + maxHeight2
}
}

return maxHeight1
}

func (s *Solution) Diameter(root *Node) int {
s.diameter = 0
s.height(root)
return s.diameter
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 723. Candy Crush
Сложность: medium

Этот вопрос касается реализации базового алгоритма исключения для Candy Crush. Дан целочисленный массив board размером m x n, представляющий сетку конфет, где board[i][j] представляет тип конфеты. Значение board[i][j] == 0 означает, что ячейка пуста. Данная доска представляет собой состояние игры после хода игрока. Теперь необходимо вернуть доску в стабильное состояние, раздавив конфеты по следующим правилам: если три или более конфет одного типа находятся рядом по вертикали или горизонтали, раздавите их все одновременно - эти позиции станут пустыми. После одновременного раздавливания всех конфет, если на пустом месте доски есть конфеты, расположенные сверху, то эти конфеты будут падать, пока не ударятся о конфету или дно одновременно. Новые конфеты не будут падать за верхнюю границу. После выполнения описанных выше действий может остаться больше конфет, которые можно раздавить. Если конфет, которые можно раздавить, больше не существует (т. е. доска стабильна), верните текущую доску. Выполняйте описанные выше правила, пока доска не станет стабильной, затем верните стабильную доску.

Пример:
Input: board = [[110,5,112,113,114],[210,211,5,213,214],[310,311,3,313,314],[410,411,412,5,414],[5,1,512,3,3],[610,4,1,613,614],[710,1,2,713,714],[810,1,2,1,1],[1,1,2,2,2],[4,1,4,4,1014]]
Output: [[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[110,0,0,0,114],[210,0,0,0,214],[310,0,0,113,314],[410,0,0,213,414],[610,211,112,313,614],[710,311,412,613,714],[810,411,512,713,1014]]


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

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

2⃣Удалите отмеченные конфеты, установив их значение в 0.

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

😎 Решение:
package main

func candyCrush(board [][]int) [][]int {
m := len(board)
n := len(board[0])
stable := false

for !stable {
stable = true
crush := make([][]bool, m)
for i := range crush {
crush[i] = make([]bool, n)
}

for i := 0; i < m; i++ {
for j := 0; j < n-2; j++ {
if abs(board[i][j]) == abs(board[i][j+1]) && abs(board[i][j+1]) == abs(board[i][j+2]) && board[i][j] != 0 {
stable = false
crush[i][j] = true
crush[i][j+1] = true
crush[i][j+2] = true
}
}
}

for i := 0; i < m-2; i++ {
for j := 0; j < n; j++ {
if abs(board[i][j]) == abs(board[i+1][j]) && abs(board[i+1][j]) == abs(board[i+2][j]) && board[i][j] != 0 {
stable = false
crush[i][j] = true
crush[i+1][j] = true
crush[i+2][j] = true
}
}
}

for i := 0; i < m; i++ {
for j := 0; j < n; j++ {
if crush[i][j] {
board[i][j] = 0
}
}
}

for j := 0; j < n; j++ {
idx := m - 1
for i := m - 1; i >= 0; i-- {
if board[i][j] != 0 {
board[idx][j] = board[i][j]
idx--
}
}
for i := idx; i >= 0; i-- {
board[i][j] = 0
}
}
}

return board
}

func abs(a int) int {
if a < 0 {
return -a
}
return a
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1
Задача: 1180. Count Substrings with Only One Distinct Letter
Сложность: easy

Дана строка s. Верните количество подстрок, которые содержат только одну уникальную букву.

Пример:
Input: s = "aaaba"
Output: 8
Explanation: The substrings with one distinct letter are "aaa", "aa", "a", "b".
"aaa" occurs 1 time.
"aa" occurs 2 times.
"a" occurs 4 times.
"b" occurs 1 time.
So the answer is 1 + 2 + 4 + 1 = 8.


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

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

2⃣Итерируйте по строке S: Если мы не достигли конца и новый символ S[right] такой же, как и начальный символ S[left], увеличьте right на 1 для дальнейшего исследования строки S; в противном случае вычислите длину подстроки как right - left и примените формулу суммы арифметической последовательности; не забудьте установить right в left для начала исследования новой подстроки.

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

😎 Решение
func countLetters(S string) int {
total := 0
left := 0

for right := 0; right <= len(S); right++ {
if right == len(S) || S[left] != S[right] {
lenSubstring := right - left
total += (1 + lenSubstring) * lenSubstring / 2
left = right
}
}

return total
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 1472. Design Browser History
Сложность: medium

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

Реализуйте класс BrowserHistory:
BrowserHistory(string homepage) Инициализирует объект с домашней страницей браузера.
void visit(string url) Посещает URL с текущей страницы. Это очищает всю историю вперёд.
string back(int steps) Перемещает на steps шагов назад в истории. Если вы можете вернуться только на x шагов в истории, а steps > x, вы вернётесь только на x шагов. Возвращает текущий URL после перемещения назад в истории на не более чем steps шагов.
string forward(int steps) Перемещает на steps шагов вперёд в истории. Если вы можете переместиться только на x шагов вперёд в истории, а steps > x, вы переместитесь только на x шагов. Возвращает текущий URL после перемещения вперёд в истории на не более чем steps шагов.

Пример:
Input:
["BrowserHistory","visit","visit","visit","back","back","forward","visit","forward","back","back"]
[["leetcode.com"],["google.com"],["facebook.com"],["youtube.com"],[1],[1],[1],["linkedin.com"],[2],[2],[7]]
Output:
[null,null,null,null,"facebook.com","google.com","facebook.com",null,"linkedin.com","google.com","leetcode.com"]

Explanation:
BrowserHistory browserHistory = new BrowserHistory("leetcode.com");
browserHistory.visit("google.com");       // You are in "leetcode.com". Visit "google.com"
browserHistory.visit("facebook.com");     // You are in "google.com". Visit "facebook.com"
browserHistory.visit("youtube.com");      // You are in "facebook.com". Visit "youtube.com"
browserHistory.back(1);                   // You are in "youtube.com", move back to "facebook.com" return "facebook.com"
browserHistory.back(1);                   // You are in "facebook.com", move back to "google.com" return "google.com"
browserHistory.forward(1);                // You are in "google.com", move forward to "facebook.com" return "facebook.com"
browserHistory.visit("linkedin.com");     // You are in "facebook.com". Visit "linkedin.com"
browserHistory.forward(2);                // You are in "linkedin.com", you cannot move forward any steps.


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

1⃣Инициализация:
Создайте класс BrowserHistory с двумя стеками (history и future) и строковой переменной current для хранения текущего URL. Инициализируйте current с домашней страницей.

2⃣Посещение URL:
Метод visit(url) сохраняет текущий URL в стеке history, устанавливает url как текущий и очищает стек future.

3⃣Навигация назад и вперед:
Метод back(steps) перемещает текущий URL в стек future и извлекает URL из стека history, пока шаги не будут исчерпаны или стек history не станет пустым.
Метод forward(steps) перемещает текущий URL в стек history и извлекает URL из стека future, пока шаги не будут исчерпаны или стек future не станет пустым.

😎 Решение:
package main

type BrowserHistory struct {
    history []string
    future  []string
    current string
}

func Constructor(homepage string) BrowserHistory {
    return BrowserHistory{
        current: homepage,
    }
}

func (this *BrowserHistory) Visit(url string) {
    this.history = append(this.history, this.current)
    this.current = url
    this.future = []string{}
}

func (this *BrowserHistory) Back(steps int) string {
    for steps > 0 && len(this.history) > 0 {
        this.future = append(this.future, this.current)
        this.current = this.history[len(this.history)-1]
        this.history = this.history[:len(this.history)-1]
        steps--
    }
    return this.current
}

func (this *BrowserHistory) Forward(steps int) string {
    for steps > 0 && len(this.future) > 0 {
        this.history = append(this.history, this.current)
        this.current = this.future[len(this.future)-1]
        this.future = this.future[:len(this.future)-1]
        steps--
    }
    return this.current
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1
Задача: 998. Maximum Binary Tree II
Сложность: medium

Максимальное дерево - это дерево, в котором каждый узел имеет значение большее, чем любое другое значение в его поддереве. Вам дан корень максимального двоичного дерева и целое число val. Как и в предыдущей задаче, данное дерево было построено из списка a (root = Construct(a)) рекурсивно с помощью следующей процедуры Construct(a): Если a пусто, верните null.
В противном случае пусть a[i] - наибольший элемент a. Создайте корневой узел со значением a[i]. Левым ребенком root будет Construct([a[0], a[1], ..., a[i - 1]]). Правым ребенком root будет Construct([a[i + 1], a[i + 2], ..., a[a.length])...., a[a.length - 1]]). Возвращаем root. Обратите внимание, что нам не было дано непосредственно a, а только корневой узел root = Construct(a). Предположим, что b - это копия a с добавленным к ней значением val. Гарантируется, что b имеет уникальные значения. Возвращаем Construct(b).

Пример:
Input: root = [5,2,4,null,1], val = 3
Output: [5,2,4,null,1,null,3]


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

1⃣Поиск места вставки:
Итерируйте через дерево, начиная с корня. Найдите место для вставки нового значения val так, чтобы дерево оставалось максимальным деревом. Если значение val больше, чем значение текущего узла, создайте новый узел с val и сделайте текущий узел его левым ребенком.

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

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

😎 Решение:
type TreeNode struct {
Val int
Left *TreeNode
Right *TreeNode
}

func insertIntoMaxTree(root *TreeNode, val int) *TreeNode {
if root == nil || val > root.Val {
return &TreeNode{Val: val, Left: root}
}
root.Right = insertIntoMaxTree(root.Right, val)
return root
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 1008. Construct Binary Search Tree from Preorder Traversal
Сложность: medium

Дается массив целых чисел preorder, который представляет собой обход BST (т.е, гарантируется, что для заданных тестовых случаев всегда можно найти дерево двоичного поиска с заданными требованиями. Дерево двоичного поиска - это двоичное дерево, в котором для каждого узла любой потомок Node.left имеет значение строго меньше, чем Node.val, а любой потомок Node.right имеет значение строго больше, чем Node.val. При обходе бинарного дерева в предварительном порядке сначала выводится значение узла, затем обход Node.left, затем обход Node.right.

Пример:
Input: preorder = [8,5,1,7,10,12]
Output: [8,5,10,1,7,null,12]


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

1⃣Инициализация переменных и функций:
Создайте класс узла дерева TreeNode с атрибутами val, left и right.
Инициализируйте индекс, который будет отслеживать текущую позицию в массиве preorder.

2⃣Рекурсивная функция для построения дерева:
Создайте рекурсивную функцию constructBST с аргументами preorder, lower и upper, которые будут ограничивать значения узлов для текущей ветви дерева.
Если текущий индекс выходит за границы массива preorder, верните null.
Получите текущее значение из preorder и проверьте, находится ли оно в пределах допустимого диапазона [lower, upper]. Если нет, верните null.

3⃣Создание узла и рекурсивное построение поддеревьев:
Создайте новый узел с текущим значением и увеличьте индекс.
Рекурсивно вызовите функцию constructBST для создания левого поддерева с обновленным верхним пределом (upper равным значению текущего узла).
Рекурсивно вызовите функцию constructBST для создания правого поддерева с обновленным нижним пределом (lower равным значению текущего узла).
Верните созданный узел.

😎 Решение:
type TreeNode struct {
Val int
Left *TreeNode
Right *TreeNode
}

func bstFromPreorder(preorder []int) *TreeNode {
index := 0
return constructBST(preorder, &index, int(^uint(0) >> 1), -int(^uint(0) >> 1) - 1)
}

func constructBST(preorder []int, index *int, upper, lower int) *TreeNode {
if *index == len(preorder) {
return nil
}

val := preorder[*index]
if val < lower || val > upper {
return nil
}

*index++
root := &TreeNode{Val: val}
root.Left = constructBST(preorder, index, val, lower)
root.Right = constructBST(preorder, index, upper, val)
return root
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 823. Binary Trees With Factors
Сложность: medium

Дан массив уникальных целых чисел arr, где каждое целое число arr[i] строго больше 1.

Мы создаем бинарное дерево, используя эти числа, и каждое число может быть использовано любое количество раз. Значение каждого не листового узла должно быть равно произведению значений его дочерних узлов.

Верните количество бинарных деревьев, которые мы можем создать. Ответ может быть слишком большим, поэтому верните ответ по модулю 10^9 + 7.

Пример:
Input: arr = [2,4]
Output: 3
Explanation: We can make these trees: [2], [4], [4, 2, 2]


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

1⃣Пусть dp[i] будет количеством способов иметь корневой узел со значением A[i]. Поскольку в приведенном примере мы всегда имеем x < v и y < v, мы можем вычислить значения dp[i] в порядке возрастания, используя динамическое программирование.

2⃣Для некоторого значения корня A[i] попробуем найти кандидатов для дочерних узлов со значениями A[j] и A[i] / A[j] (так, чтобы очевидно A[j] * (A[i] / A[j]) = A[i]). Для быстрой реализации этого нам понадобится индекс, который ищет это значение: если A[k] = A[i] / A[j], тогда index[A[i] / A[j]] = k.

3⃣После этого добавим все возможные dp[j] * dp[k] (с j < i, k < i) к нашему ответу dp[i]. В нашей реализации на Java мы тщательно использовали long, чтобы избежать проблем с переполнением.

😎 Решение:
func numFactoredBinaryTrees(A []int) int {
const MOD = 1_000_000_007
sort.Ints(A)
dp := make([]int64, len(A))
for i := range dp {
dp[i] = 1
}
index := make(map[int]int)
for i, x := range A {
index[x] = i
}

for i, x := range A {
for j := 0; j < i; j++ {
if x%A[j] == 0 {
right := x / A[j]
if k, ok := index[right]; ok {
dp[i] = (dp[i] + dp[j]*dp[k]) % MOD
}
}
}
}

result := int64(0)
for _, x := range dp {
result = (result + x) % MOD
}
return int(result)
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
💊1
Задача: 303. Range Sum Query - Immutable
Сложность: easy

Дан целочисленный массив nums. Обработайте несколько запросов следующего типа:
Вычислите сумму элементов массива nums между индексами left и right включительно, где left <= right.
Реализуйте класс NumArray:
- NumArray(int[] nums) Инициализирует объект с целочисленным массивом nums.
- int sumRange(int left, int right) Возвращает сумму элементов массива nums между индексами left и right включительно (т.е. nums[left] + nums[left + 1] + ... + nums[right]).

Пример:
Input
["NumArray", "sumRange", "sumRange", "sumRange"]
[[[-2, 0, 3, -5, 2, -1]], [0, 2], [2, 5], [0, 5]]
Output
[null, 1, -1, -3]


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

1⃣Инициализация:
Создайте массив sum длиной на один элемент больше, чем массив nums, и заполните его накопленными суммами элементов массива nums.

2⃣Предварительное вычисление сумм:
Заполните массив sum, где каждый элемент sum[i + 1] является суммой всех предыдущих элементов массива nums до индекса i включительно.

3⃣Вычисление диапазонной суммы:
Для каждого запроса суммы элементов между индексами left и right используйте разницу между sum[right + 1] и sum[left], чтобы быстро получить результат.

😎 Решение:
package main

type NumArray struct {
sum []int
}

func Constructor(nums []int) NumArray {
sum := make([]int, len(nums)+1)
for i := 0; i < len(nums); i++ {
sum[i+1] = sum[i] + nums[i]
}
return NumArray{sum}
}

func (this *NumArray) SumRange(i int, j int) int {
return this.sum[j+1] - this.sum[i]
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 521. Longest Uncommon Subsequence I
Сложность: easy

Даны две строки a и b. Верните длину самой длинной несообщей подпоследовательности.
Несообщая подпоследовательность — это строка, которая является подпоследовательностью только одной из двух строк.

Пример:
Input: a = "aba", b = "cdc"  
Output: 3


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

1⃣Проверяем, равны ли строки a и b. Если они полностью совпадают, то все подпоследовательности у них одинаковы, и несообщей не существует — возвращаем -1.

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

3⃣Возвращаем длину более длинной строки, так как это и будет длина самой длинной несообщей подпоследовательности.

😎 Решение:
func findLUSlength(a string, b string) int {
if a == b {
return -1
} else {
if len(a) > len(b) {
return len(a)
} else {
return len(b)
}
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 1192. Critical Connections in a Network
Сложность: hard

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

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

Пример:
Input: n = 4, connections = [[0,1],[1,2],[2,0],[1,3]]
Output: [[1,3]]
Explanation: [[3,1]] is also accepted.


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

1⃣Построение графа и инициализация:
Постройте граф в виде списка смежности и создайте словарь для хранения соединений.
Инициализируйте ранги для узлов.

2⃣Поиск в глубину (DFS):
Реализуйте функцию dfs для обхода графа.
Обновите ранги узлов и определите минимальный ранг для текущего узла.
Проверьте, можно ли удалить текущее соединение, и обновите минимальный ранг.

3⃣Поиск критических соединений:
После завершения обхода DFS преобразуйте оставшиеся соединения в список и верните его.

😎 Решение:
type Solution struct {
graph map[int][]int
rank map[int]int
connDict map[[2]int]bool
}

func (s *Solution) CriticalConnections(n int, connections [][]int) [][]int {
s.formGraph(n, connections)
s.dfs(0, 0)

var result [][]int
for conn := range s.connDict {
result = append(result, []int{conn[0], conn[1]})
}
return result
}

func (s *Solution) dfs(node, discoveryRank int) int {
if r, ok := s.rank[node]; ok {
return r
}

s.rank[node] = discoveryRank
minRank := discoveryRank + 1

for _, neighbor := range s.graph[node] {
if neighRank, ok := s.rank[neighbor]; ok && neighRank == discoveryRank-1 {
continue
}

recursiveRank := s.dfs(neighbor, discoveryRank+1)

if recursiveRank <= discoveryRank {
delete(s.connDict, [2]int{min(node, neighbor), max(node, neighbor)})
}

minRank = min(minRank, recursiveRank)
}

return minRank
}

func (s *Solution) formGraph(n int, connections [][]int) {
s.graph = make(map[int][]int)
s.rank = make(map[int]int)
s.connDict = make(map[[2]int]bool)

for i := 0; i < n; i++ {
s.graph[i] = []int{}
}

for _, edge := range connections {
u, v := edge[0], edge[1]
s.graph[u] = append(s.graph[u], v)
s.graph[v] = append(s.graph[v], u)
s.connDict[[2]int{min(u, v), max(u, v)}] = true
}
}

func min(a, b int) int {
if a < b {
return a
}
return b
}

func max(a, b int) int {
if a > b {
return a
}
return b
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 1198. Find Smallest Common Element in All Rows
Сложность: medium

Дана матрица mat размером m x n, где каждая строка отсортирована в строго возрастающем порядке. Верните наименьший общий элемент во всех строках.

Если общего элемента нет, верните -1.

Пример:
Input: mat = [[1,2,3,4,5],[2,4,5,8,10],[3,5,7,9,11],[1,3,5,7,9]]
Output: 5


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

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

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

3⃣Проверка счетчика:
Если счетчик равен количеству строк, возвращайте текущий максимум.
Повторите шаг 2.

😎 Решение:
func smallestCommonElement(mat [][]int) int {
n := len(mat)
m := len(mat[0])
pos := make([]int, n)
cur_max := 0
cnt := 0

for {
for i := 0; i < n; i++ {
for pos[i] < m && mat[i][pos[i]] < cur_max {
pos[i]++
}
if pos[i] >= m {
return -1
}
if mat[i][pos[i]] != cur_max {
cnt = 1
cur_max = mat[i][pos[i]]
} else {
cnt++
if cnt == n {
return cur_max
}
}
}
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 474. Ones and Zeroes
Сложность: medium

Дан массив двоичных строк strs и два целых числа m и n.
Нужно вернуть размер наибольшего подмножества strs, такого что в нём содержится не более `m` нулей и не более `n` единиц.

Пример:
Input: strs = ["10","0001","111001","1","0"], m = 5, n = 3 
Output: 4

Explanation: Подмножество {"10", "0001", "1", "0"} содержит 5 нулей и 3 единицы — это максимум по условию. Размер = 4.

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

1⃣Рассматриваем все возможные подмножества с помощью побитовой маски: от 0 до 2^len(strs).

2⃣Для каждой маски определяем, какие строки входят в подмножество. Считаем количество 0 и 1.

3⃣Если сумма не превышает m и n — учитываем длину подмножества как кандидата в ответ.

😎 Решение:
package main

import (
"fmt"
)

type Solution struct{}

func (s *Solution) findMaxForm(strs []string, m int, n int) int {
maxlen := 0
for i := 0; i < (1 << len(strs)); i++ {
zeroes, ones, length := 0, 0, 0
for j := 0; j < len(strs); j++ {
if (i & (1 << j)) != 0 {
count := s.countZeroesOnes(strs[j])
zeroes += count[0]
ones += count[1]
if zeroes > m || ones > n {
break
}
length++
}
}
if zeroes <= m && ones <= n {
maxlen = max(maxlen, length)
}
}
return maxlen
}

func (s *Solution) countZeroesOnes(str string) [2]int {
count := [2]int{}
for _, char := range str {
count[char-'0']++
}
return count
}

func max(a, b int) int {
if a > b {
return a
}
return b
}

func main() {
sol := Solution{}
fmt.Println(sol.findMaxForm([]string{"10", "0001", "111001", "1", "0"}, 5, 3))
}


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