Задача: 1178. Number of Valid Words for Each PuzzleHardTopicsHint
Сложность: hard
Относительно заданной строки-головоломки, слово является допустимым, если выполняются оба следующих условия:
Слово содержит первую букву головоломки.
Каждая буква в слове присутствует в головоломке.
Например, если головоломка "abcdefg", то допустимыми словами являются "faced", "cabbage" и "baggage", а недопустимыми словами являются "beefed" (не включает 'a') и "based" (включает 's', которой нет в головоломке).
Верните массив answer, где answer[i] - это количество слов в данном списке слов words, которые допустимы относительно головоломки puzzles[i].
Пример:
👨💻 Алгоритм:
1⃣ Постройте карту. Для каждого слова в списке words:
Преобразуйте его в битовую маску его символов. Если эта битовая маска не была ранее встречена, сохраните ее в качестве ключа в карте со значением 1. Если она была ранее встречена, увеличьте счетчик для этой битовой маски на 1.
2⃣ Подсчитайте количество допустимых слов для каждой головоломки. Для каждой головоломки в списке puzzles:
Преобразуйте ее в битовую маску ее символов. Итерируйте по каждой возможной подмаске, содержащей первую букву головоломки (puzzle[i][0]). Слово является допустимым для головоломки, если его битовая маска совпадает с одной из подмасок головоломки.
3⃣ Для каждой подмаски увеличивайте счетчик на количество слов, соответствующих подмаске. Мы можем найти количество слов, соответствующих подмаске, используя карту, построенную на предыдущем шаге.
😎 Решение
Ставь 👍 и забирай 📚 Базу знаний
Сложность: hard
Относительно заданной строки-головоломки, слово является допустимым, если выполняются оба следующих условия:
Слово содержит первую букву головоломки.
Каждая буква в слове присутствует в головоломке.
Например, если головоломка "abcdefg", то допустимыми словами являются "faced", "cabbage" и "baggage", а недопустимыми словами являются "beefed" (не включает 'a') и "based" (включает 's', которой нет в головоломке).
Верните массив answer, где answer[i] - это количество слов в данном списке слов words, которые допустимы относительно головоломки puzzles[i].
Пример:
Input: words = ["apple","pleas","please"], puzzles = ["aelwxyz","aelpxyz","aelpsxy","saelpxy","xaelpsy"]
Output: [0,1,3,2,0]
Преобразуйте его в битовую маску его символов. Если эта битовая маска не была ранее встречена, сохраните ее в качестве ключа в карте со значением 1. Если она была ранее встречена, увеличьте счетчик для этой битовой маски на 1.
Преобразуйте ее в битовую маску ее символов. Итерируйте по каждой возможной подмаске, содержащей первую букву головоломки (puzzle[i][0]). Слово является допустимым для головоломки, если его битовая маска совпадает с одной из подмасок головоломки.
func bitmask(word string) int {
mask := 0
for _, letter := range word {
mask |= 1 << (letter - 'a')
}
return mask
}
func findNumOfValidWords(words []string, puzzles []string) []int {
wordCount := make(map[int]int)
for _, word := range words {
mask := bitmask(word)
wordCount[mask]++
}
result := make([]int, 0, len(puzzles))
for _, puzzle := range puzzles {
first := 1 << (puzzle[0] - 'a')
count := wordCount[first]
mask := bitmask(puzzle[1:])
for submask := mask; submask > 0; submask = (submask - 1) & mask {
count += wordCount[submask | first]
}
result = append(result, count)
}
return result
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 1203. Sort Items by Groups Respecting Dependencies
Сложность: hard
Есть n предметов, каждый из которых принадлежит нулевой или одной из m групп, где group[i] — это группа, к которой принадлежит i-й предмет, и равно -1, если i-й предмет не принадлежит никакой группе. Предметы и группы имеют индексацию с нуля. Группа может не иметь ни одного предмета.
Верните отсортированный список предметов таким образом:
Предметы, принадлежащие одной группе, расположены рядом друг с другом в отсортированном списке.
Существуют некоторые отношения между этими предметами, где beforeItems[i] — это список, содержащий все предметы, которые должны быть перед i-м предметом в отсортированном массиве (слева от i-го предмета).
Верните любое решение, если существует более одного решения, и верните пустой список, если решения не существует.
Пример:
👨💻 Алгоритм:
1⃣ Инициализация и создание графов:
Присвоить уникальные идентификаторы группам для элементов без группы.
Создать два графа: item_graph для элементов и group_graph для групп. Также создать два массива для учета входящих рёбер для элементов и групп.
2⃣ Построение графов:
Пройти по массиву beforeItems и добавить зависимости между элементами в item_graph, увеличивая счётчик входящих рёбер.
Если элементы принадлежат разным группам, добавить зависимость между группами в group_graph, увеличивая счётчик входящих рёбер.
3⃣ Топологическая сортировка и создание итогового списка:
Выполнить топологическую сортировку для элементов и групп. Если есть цикл, вернуть пустой список.
Создать итоговый список, добавляя отсортированные элементы каждой группы.
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: hard
Есть n предметов, каждый из которых принадлежит нулевой или одной из m групп, где group[i] — это группа, к которой принадлежит i-й предмет, и равно -1, если i-й предмет не принадлежит никакой группе. Предметы и группы имеют индексацию с нуля. Группа может не иметь ни одного предмета.
Верните отсортированный список предметов таким образом:
Предметы, принадлежащие одной группе, расположены рядом друг с другом в отсортированном списке.
Существуют некоторые отношения между этими предметами, где beforeItems[i] — это список, содержащий все предметы, которые должны быть перед i-м предметом в отсортированном массиве (слева от i-го предмета).
Верните любое решение, если существует более одного решения, и верните пустой список, если решения не существует.
Пример:
Input: n = 8, m = 2, group = [-1,-1,1,0,0,1,0,-1], beforeItems = [[],[6],[5],[6],[3,6],[],[],[]]
Output: [6,3,4,1,5,2,0,7]
Присвоить уникальные идентификаторы группам для элементов без группы.
Создать два графа: item_graph для элементов и group_graph для групп. Также создать два массива для учета входящих рёбер для элементов и групп.
Пройти по массиву beforeItems и добавить зависимости между элементами в item_graph, увеличивая счётчик входящих рёбер.
Если элементы принадлежат разным группам, добавить зависимость между группами в group_graph, увеличивая счётчик входящих рёбер.
Выполнить топологическую сортировку для элементов и групп. Если есть цикл, вернуть пустой список.
Создать итоговый список, добавляя отсортированные элементы каждой группы.
func sortItems(n int, m int, group []int, beforeItems [][]int) []int {
groupId := m
for i := 0; i < n; i++ {
if group[i] == -1 {
group[i] = groupId
groupId++
}
}
itemGraph := make(map[int][]int)
groupGraph := make(map[int][]int)
itemIndegree := make([]int, n)
groupIndegree := make([]int, groupId)
for i := 0; i < n; i++ {
itemGraph[i] = []int{}
}
for i := 0; i < groupId; i++ {
groupGraph[i] = []int{}
}
for curr := 0; curr < n; curr++ {
for _, prev := range beforeItems[curr] {
itemGraph[prev] = append(itemGraph[prev], curr)
itemIndegree[curr]++
if group[curr] != group[prev] {
groupGraph[group[prev]] = append(groupGraph[group[prev]], group[curr])
groupIndegree[group[curr]]++
}
}
}
itemOrder := topologicalSort(itemGraph, itemIndegree)
groupOrder := topologicalSort(groupGraph, groupIndegree)
if len(itemOrder) == 0 || len(groupOrder) == 0 {
return []int{}
}
orderedGroups := make(map[int][]int)
for _, item := range itemOrder {
orderedGroups[group[item]] = append(orderedGroups[group[item]], item)
}
answerList := []int{}
for _, groupIndex := range groupOrder {
answerList = append(answerList, orderedGroups[groupIndex]...)
}
return answerList
}
func topologicalSort(graph map[int][]int, indegree []int) []int {
visited := []int{}
stack := []int{}
for key := range graph {
if indegree[key] == 0 {
stack = append(stack, key)
}
}
for len(stack) > 0 {
curr := stack[len(stack)-1]
stack = stack[:len(stack)-1]
visited = append(visited, curr)
for _, next := range graph[curr] {
indegree[next]--
if indegree[next] == 0 {
stack = append(stack, next)
}
}
}
if len(visited) != len(graph) {
return []int{}
}
return visited
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 110. Balanced Binary Tree
Сложность: medium
Дано бинарное дерево, определите, является ли оно
сбалансированным по высоте.
Пример:
👨💻 Алгоритм:
1⃣ Сначала мы определяем функцию height, которая для любого узла p в дереве T возвращает:
-1, если p является пустым поддеревом, т.е. null;
1 + max(height(p.left), height(p.right)) в противном случае.
2⃣ Теперь, когда у нас есть метод для определения высоты дерева, остается только сравнить высоты детей каждого узла. Дерево T с корнем r является сбалансированным тогда и только тогда, когда высоты его двух детей отличаются не более чем на 1 и поддеревья каждого ребенка также сбалансированы.
3⃣ Следовательно, мы можем сравнить высоты двух дочерних поддеревьев, а затем рекурсивно проверить каждое из них:
Если root == NULL, возвращаем true.
Если abs(height(root.left) - height(root.right)) > 1, возвращаем false.
В противном случае возвращаем isBalanced(root.left) && isBalanced(root.right).
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: medium
Дано бинарное дерево, определите, является ли оно
сбалансированным по высоте.
Пример:
Input: root = [3,9,20,null,null,15,7]
Output: true
-1, если p является пустым поддеревом, т.е. null;
1 + max(height(p.left), height(p.right)) в противном случае.
Если root == NULL, возвращаем true.
Если abs(height(root.left) - height(root.right)) > 1, возвращаем false.
В противном случае возвращаем isBalanced(root.left) && isBalanced(root.right).
func height(root *TreeNode) int {
if root == nil {
return -1
}
return 1 + max(height(root.Left), height(root.Right))
}
func isBalanced(root *TreeNode) bool {
if root == nil {
return true
}
return abs(height(root.Left)-height(root.Right)) < 2 &&
isBalanced(root.Left) &&
isBalanced(root.Right)
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
func abs(a int) int {
if a < 0 {
return -a
}
return a
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 513. Find Bottom Left Tree Value
Сложность: medium
Дан корень двоичного дерева, верните самое левое значение в последней строке дерева.
Пример:
👨💻 Алгоритм:
1⃣ Инициализируйте переменную maxDepth для хранения глубины нижнего уровня дерева. Инициализируйте переменную bottomLeftValue для хранения самого левого значения в последней строке дерева.
2⃣ Реализуйте рекурсивную функцию dfs, которая обходит дерево и находит самое левое значение в последней строке дерева. Параметры функции: current (текущий узел) и depth (его глубина). Проверьте, пуст ли текущий узел. Если да, то вернитесь.
3⃣ Проверьте, превышает ли текущая глубина глобальную переменную maxDepth. Если да, это значит, что мы нашли новый уровень. Установите maxDepth в значение текущей глубины. Установите bottomLeftValue в значение текущего узла. Рекурсивно вызовите dfs для левого поддерева текущего узла, увеличив глубину на один. Рекурсивно вызовите dfs для правого поддерева текущего узла, увеличив глубину на один. Вызовите dfs с корнем и начальной глубиной 0. Верните bottomLeftValue.
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: medium
Дан корень двоичного дерева, верните самое левое значение в последней строке дерева.
Пример:
Input: root = [2,1,3]
Output: 1
type TreeNode struct {
Val int
Left *TreeNode
Right *TreeNode
}
type Solution struct {
maxDepth int
bottomLeftValue int
}
func (s *Solution) findBottomLeftValue(root *TreeNode) int {
s.maxDepth = -1
s.bottomLeftValue = 0
s.dfs(root, 0)
return s.bottomLeftValue
}
func (s *Solution) dfs(current *TreeNode, depth int) {
if current == nil {
return
}
if depth > s.maxDepth {
s.maxDepth = depth
s.bottomLeftValue = current.Val
}
s.dfs(current.Left, depth+1)
s.dfs(current.Right, depth+1)
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 1020. Number of Enclaves
Сложность: medium
Вам дана двоичная матричная сетка m x n, где 0 обозначает морскую ячейку, а 1 - сухопутную. Ход состоит из перехода от одной сухопутной ячейки к другой соседней (в 4-х направлениях) или выхода за границу сетки. Верните количество сухопутных ячеек в сетке, для которых мы не можем выйти за границу сетки за любое количество ходов.
Пример:
👨💻 Алгоритм:
1⃣ Обработка граничных сухопутных ячеек:
Пройдитесь по всем ячейкам, которые находятся на границе сетки (первый и последний ряды, первый и последний столбцы). Если ячейка содержит 1, начните поиск в глубину (DFS) или поиск в ширину (BFS), чтобы пометить все достижимые из нее сухопутные ячейки как посещенные.
2⃣ Проверка всех ячеек:
Пройдите по всем ячейкам матрицы, считая количество сухопутных ячеек, которые не были посещены в предыдущем шаге.
3⃣ Возврат результата:
Верните количество не посещенных сухопутных ячеек.
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: medium
Вам дана двоичная матричная сетка m x n, где 0 обозначает морскую ячейку, а 1 - сухопутную. Ход состоит из перехода от одной сухопутной ячейки к другой соседней (в 4-х направлениях) или выхода за границу сетки. Верните количество сухопутных ячеек в сетке, для которых мы не можем выйти за границу сетки за любое количество ходов.
Пример:
Input: grid = [[0,0,0,0],[1,0,1,0],[0,1,1,0],[0,0,0,0]]
Output: 3
Пройдитесь по всем ячейкам, которые находятся на границе сетки (первый и последний ряды, первый и последний столбцы). Если ячейка содержит 1, начните поиск в глубину (DFS) или поиск в ширину (BFS), чтобы пометить все достижимые из нее сухопутные ячейки как посещенные.
Пройдите по всем ячейкам матрицы, считая количество сухопутных ячеек, которые не были посещены в предыдущем шаге.
Верните количество не посещенных сухопутных ячеек.
func numEnclaves(grid [][]int) int {
m, n := len(grid), len(grid[0])
var dfs func(x, y int)
dfs = func(x, y int) {
if x < 0 || y < 0 || x >= m || y >= n || grid[x][y] != 1 {
return
}
grid[x][y] = 0
dfs(x+1, y)
dfs(x-1, y)
dfs(x, y+1)
dfs(x, y-1)
}
for i := 0; i < m; i++ {
if grid[i][0] == 1 {
dfs(i, 0)
}
if grid[i][n-1] == 1 {
dfs(i, n-1)
}
}
for j := 0; j < n; j++ {
if grid[0][j] == 1 {
dfs(0, j)
}
if grid[m-1][j] == 1 {
dfs(m-1, j)
}
}
count := 0
for i := 0; i < m; i++ {
for j := 0; j < n; j++ {
if grid[i][j] == 1 {
count++
}
}
}
return count
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 748. Shortest Completing Word
Сложность: easy
Вам дан целочисленный массив nums, в котором наибольшее целое число уникально. Определите, является ли наибольший элемент массива по крайней мере в два раза больше всех остальных чисел в массиве. Если да, то верните индекс самого большого элемента, в противном случае верните -1.
Пример:
👨💻 Алгоритм:
1⃣ Извлечь все буквы из licensePlate, игнорируя цифры и пробелы, и создать словарь для подсчета частоты каждой буквы.
2⃣ Пройти по массиву words, проверяя каждое слово на соответствие требованиям.
3⃣ Найти самое короткое завершающее слово среди подходящих.
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: easy
Вам дан целочисленный массив nums, в котором наибольшее целое число уникально. Определите, является ли наибольший элемент массива по крайней мере в два раза больше всех остальных чисел в массиве. Если да, то верните индекс самого большого элемента, в противном случае верните -1.
Пример:
Input: licensePlate = "1s3 PSt", words = ["step","steps","stripe","stepple"]
Output: "steps"
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 одинаковы.
Пример:
👨💻 Алгоритм:
1⃣ Инициализация:
Создайте словарь сокращений abbrDict, который будет хранить сокращения слов в виде ключей и булевы значения, указывающие, уникально ли сокращение.
Создайте множество dict, содержащее все слова из словаря, чтобы быстро проверять наличие слова в словаре.
2⃣ Генерация сокращений:
При инициализации объекта ValidWordAbbr пройдите через каждое слово в словаре и создайте его сокращение.
Если сокращение уже существует в abbrDict, установите значение в false (не уникальное). В противном случае установите значение в true (уникальное).
3⃣ Проверка уникальности:
Для метода isUnique создайте сокращение для входного слова и проверьте, есть ли это сокращение в abbrDict.
Если сокращение отсутствует в abbrDict, возвращайте true.
Если сокращение присутствует и оно уникально, проверьте, есть ли это слово в словаре. Если да, возвращайте true, в противном случае - false.
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: 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.
Создайте словарь сокращений abbrDict, который будет хранить сокращения слов в виде ключей и булевы значения, указывающие, уникально ли сокращение.
Создайте множество dict, содержащее все слова из словаря, чтобы быстро проверять наличие слова в словаре.
При инициализации объекта ValidWordAbbr пройдите через каждое слово в словаре и создайте его сокращение.
Если сокращение уже существует в abbrDict, установите значение в false (не уникальное). В противном случае установите значение в true (уникальное).
Для метода 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, в котором каждый элемент встречается три раза, кроме одного, который встречается ровно один раз. Найдите этот единственный элемент и верните его.
Вы должны реализовать решение с линейной сложностью выполнения и использовать только постоянное дополнительное пространство.
Пример:
👨💻 Алгоритм:
1⃣ Сортировка массива:
Отсортируйте массив nums. Это упорядочит все элементы так, чтобы одинаковые числа находились рядом.
2⃣ Итерация с проверкой:
Используйте цикл for для перебора элементов массива от начала до nums.size() - 2 с шагом 3. Таким образом, каждый проверяемый индекс будет иметь следующий за ним индекс в пределах массива.
Если элемент на текущем индексе совпадает с элементом на следующем индексе (проверка nums[i] == nums[i + 1]), продолжайте следующую итерацию цикла.
3⃣ Возврат уникального элемента:
Если элемент на текущем индексе не совпадает с следующим, значит, это искомый уникальный элемент, который встречается только один раз. В этом случае возвращайте элемент на текущем индексе.
Если до последнего элемента цикл не нашёл уникального элемента, возвращайте последний элемент массива nums[nums.size() - 1], поскольку он, очевидно, будет уникальным, если предыдущие проверки не выявили уникального элемента раньше.
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: medium
Дан массив целых чисел nums, в котором каждый элемент встречается три раза, кроме одного, который встречается ровно один раз. Найдите этот единственный элемент и верните его.
Вы должны реализовать решение с линейной сложностью выполнения и использовать только постоянное дополнительное пространство.
Пример:
Input: nums = [2,2,3,2]
Output: 3
Отсортируйте массив nums. Это упорядочит все элементы так, чтобы одинаковые числа находились рядом.
Используйте цикл for для перебора элементов массива от начала до nums.size() - 2 с шагом 3. Таким образом, каждый проверяемый индекс будет иметь следующий за ним индекс в пределах массива.
Если элемент на текущем индексе совпадает с элементом на следующем индексе (проверка nums[i] == nums[i + 1]), продолжайте следующую итерацию цикла.
Если элемент на текущем индексе не совпадает с следующим, значит, это искомый уникальный элемент, который встречается только один раз. В этом случае возвращайте элемент на текущем индексе.
Если до последнего элемента цикл не нашёл уникального элемента, возвращайте последний элемент массива 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() в обходе всегда будет хотя бы одно следующее число.
Пример:
👨💻 Алгоритм:
1⃣ Инициализируйте пустой массив, который будет содержать узлы бинарного дерева поиска в отсортированном порядке.
2⃣ Мы обходим бинарное дерево поиска в порядке in-order и для каждого узла, который обрабатываем, добавляем его в наш массив узлов. Обратите внимание, что перед обработкой узла сначала нужно обработать (или рекурсивно вызвать) его левое поддерево, а после обработки узла — его правое поддерево.
3⃣ Когда у нас будут все узлы в массиве, нам просто нужен указатель или индекс в этом массиве для реализации двух функций
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: 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
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) можно удалить несколько узлов, верните такой узел с наименьшим индексом.
Пример:
👨💻 Алгоритм:
1⃣ Определить компоненты связности в графе.
Для каждой компоненты связности определить количество зараженных узлов и общее количество узлов.
2⃣ Для каждого узла в initial удалить его и пересчитать количество зараженных узлов.
3⃣ Найти узел, удаление которого минимизирует количество зараженных узлов. Если несколько узлов минимизируют количество зараженных узлов одинаково, выбрать узел с наименьшим индексом.
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: 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
Для каждой компоненты связности определить количество зараженных узлов и общее количество узлов.
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.
Пример:
👨💻 Алгоритм:
1⃣ Найдите более короткую строку среди str1 и str2, для простоты пусть это будет str1.
2⃣ Начните с base = str1 и проверьте, состоят ли обе строки str1 и str2 из множителей строки base. Если это так, верните base. В противном случае, попробуйте более короткую строку, удалив последний символ из base.
3⃣ Если вы проверили все префиксные строки и не нашли строку GCD, верните "".
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: easy
Для двух строк s и t мы говорим, что "t делит s", если и только если s = t + t + t + ... + t (т.е. t соединена сама с собой один или более раз).
Даны две строки str1 и str2, верните наибольшую строку x, такую что x делит и str1, и str2.
Пример:
Input: str1 = "ABCABC", str2 = "ABC"
Output: "ABC"
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), верните минимальную абсолютную разницу между значениями любых двух разных узлов в дереве.
Пример:
👨💻 Алгоритм:
1⃣ Обход дерева в порядке возрастания (инфиксный обход):
Создайте список inorderNodes для хранения значений узлов.
Выполните инфиксный обход дерева, добавляя значения узлов в список.
2⃣ Нахождение минимальной разницы:
Создайте переменную minDifference и инициализируйте её бесконечностью.
Пройдите по списку inorderNodes, начиная с индекса 1, и найдите минимальную абсолютную разницу между последовательными элементами.
3⃣ Возврат результата:
Верните minDifference.
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: easy
Дано корень бинарного дерева поиска (BST), верните минимальную абсолютную разницу между значениями любых двух разных узлов в дереве.
Пример:
Input: root = [4,2,6,1,3]
Output: 1
Создайте список inorderNodes для хранения значений узлов.
Выполните инфиксный обход дерева, добавляя значения узлов в список.
Создайте переменную minDifference и инициализируйте её бесконечностью.
Пройдите по списку inorderNodes, начиная с индекса 1, и найдите минимальную абсолютную разницу между последовательными элементами.
Верните 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 в противном случае.
Пример:
👨💻 Алгоритм:
1⃣ Преобразуй число в строку для удобства работы с его цифрами.
Используй словарь для хранения соответствий цифр при повороте на 180 градусов.
2⃣ Пройди по цифрам числа, проверяя, что все цифры действительны и заменяя их на соответствующие при повороте.
3⃣ Проверь, что перевернутая строка отличается от исходной.
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: 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
Используй словарь для хранения соответствий цифр при повороте на 180 градусов.
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-арное дерево, нужно найти диаметр — длину самого длинного пути между двумя узлами дерева (не обязательно проходящего через корень).
Пример:
👨💻 Алгоритм:
1⃣ Рекурсивная функция высоты
Реализуем
2⃣ Нахождение двух максимальных высот
Для каждого узла определяем две максимальные высоты среди всех его дочерних. Их сумма — возможный диаметр, проходящий через текущий узел.
3⃣ Обновление диаметра
Если сумма двух наибольших высот больше текущего значения
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: medium
Дано N-арное дерево, нужно найти диаметр — длину самого длинного пути между двумя узлами дерева (не обязательно проходящего через корень).
Пример:
Input: root = [1,null,3,2,4,null,5,6]
Output: 3
Реализуем
height(node) — возвращает максимальную высоту поддерева. Для листа это 0. Для внутреннего узла — 1 + макс(высота дочернего).Для каждого узла определяем две максимальные высоты среди всех его дочерних. Их сумма — возможный диаметр, проходящий через текущий узел.
Если сумма двух наибольших высот больше текущего значения
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 означает, что ячейка пуста. Данная доска представляет собой состояние игры после хода игрока. Теперь необходимо вернуть доску в стабильное состояние, раздавив конфеты по следующим правилам: если три или более конфет одного типа находятся рядом по вертикали или горизонтали, раздавите их все одновременно - эти позиции станут пустыми. После одновременного раздавливания всех конфет, если на пустом месте доски есть конфеты, расположенные сверху, то эти конфеты будут падать, пока не ударятся о конфету или дно одновременно. Новые конфеты не будут падать за верхнюю границу. После выполнения описанных выше действий может остаться больше конфет, которые можно раздавить. Если конфет, которые можно раздавить, больше не существует (т. е. доска стабильна), верните текущую доску. Выполняйте описанные выше правила, пока доска не станет стабильной, затем верните стабильную доску.
Пример:
👨💻 Алгоритм:
1⃣ Найдите все группы из трех или более одинаковых конфет, как в горизонтальном, так и в вертикальном направлениях, и отметьте их для удаления.
2⃣ Удалите отмеченные конфеты, установив их значение в 0.
3⃣ Переместите конфеты вниз, чтобы заполнить пустые ячейки, и повторите процесс, пока не останется групп конфет для удаления.
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: 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]]
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. Верните количество подстрок, которые содержат только одну уникальную букву.
Пример:
👨💻 Алгоритм:
1⃣ Инициализируйте целочисленную переменную total для подсчета количества подстрок, а также два указателя left и right, которые отмечают начало и конец подстроки, содержащей только одну уникальную букву.
2⃣ Итерируйте по строке S: Если мы не достигли конца и новый символ S[right] такой же, как и начальный символ S[left], увеличьте right на 1 для дальнейшего исследования строки S; в противном случае вычислите длину подстроки как right - left и примените формулу суммы арифметической последовательности; не забудьте установить right в left для начала исследования новой подстроки.
3⃣ После завершения итерации добавьте к total количество подстрок для последнего сегмента, если он не был учтен. Верните значение total.
😎 Решение
Ставь 👍 и забирай 📚 Базу знаний
Сложность: 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.
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 шагов.
Пример:
👨💻 Алгоритм:
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 не станет пустым.
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: 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.
Создайте класс BrowserHistory с двумя стеками (history и future) и строковой переменной current для хранения текущего URL. Инициализируйте current с домашней страницей.
Метод visit(url) сохраняет текущий URL в стеке history, устанавливает url как текущий и очищает стек future.
Метод 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).
Пример:
👨💻 Алгоритм:
1⃣ Поиск места вставки:
Итерируйте через дерево, начиная с корня. Найдите место для вставки нового значения val так, чтобы дерево оставалось максимальным деревом. Если значение val больше, чем значение текущего узла, создайте новый узел с val и сделайте текущий узел его левым ребенком.
2⃣ Вставка нового узла:
Если значение val меньше, чем значение текущего узла, продолжайте спускаться по правому поддереву, пока не найдете место для вставки.
3⃣ Создание нового дерева:
После вставки нового узла убедитесь, что дерево сохраняет свои свойства максимального дерева.
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: 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]
Итерируйте через дерево, начиная с корня. Найдите место для вставки нового значения val так, чтобы дерево оставалось максимальным деревом. Если значение val больше, чем значение текущего узла, создайте новый узел с val и сделайте текущий узел его левым ребенком.
Если значение val меньше, чем значение текущего узла, продолжайте спускаться по правому поддереву, пока не найдете место для вставки.
После вставки нового узла убедитесь, что дерево сохраняет свои свойства максимального дерева.
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.
Пример:
👨💻 Алгоритм:
1⃣ Инициализация переменных и функций:
Создайте класс узла дерева TreeNode с атрибутами val, left и right.
Инициализируйте индекс, который будет отслеживать текущую позицию в массиве preorder.
2⃣ Рекурсивная функция для построения дерева:
Создайте рекурсивную функцию constructBST с аргументами preorder, lower и upper, которые будут ограничивать значения узлов для текущей ветви дерева.
Если текущий индекс выходит за границы массива preorder, верните null.
Получите текущее значение из preorder и проверьте, находится ли оно в пределах допустимого диапазона [lower, upper]. Если нет, верните null.
3⃣ Создание узла и рекурсивное построение поддеревьев:
Создайте новый узел с текущим значением и увеличьте индекс.
Рекурсивно вызовите функцию constructBST для создания левого поддерева с обновленным верхним пределом (upper равным значению текущего узла).
Рекурсивно вызовите функцию constructBST для создания правого поддерева с обновленным нижним пределом (lower равным значению текущего узла).
Верните созданный узел.
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: 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]
Создайте класс узла дерева TreeNode с атрибутами val, left и right.
Инициализируйте индекс, который будет отслеживать текущую позицию в массиве preorder.
Создайте рекурсивную функцию constructBST с аргументами preorder, lower и upper, которые будут ограничивать значения узлов для текущей ветви дерева.
Если текущий индекс выходит за границы массива preorder, верните null.
Получите текущее значение из preorder и проверьте, находится ли оно в пределах допустимого диапазона [lower, upper]. Если нет, верните null.
Создайте новый узел с текущим значением и увеличьте индекс.
Рекурсивно вызовите функцию 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.
Пример:
👨💻 Алгоритм:
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, чтобы избежать проблем с переполнением.
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: 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]
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