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

Тесты t.iss.one/+icUwivvbGOkwNWRi
Вопросы собесов t.iss.one/+7ESm0VKXC4tjYzky
Вакансии t.iss.one/+4pspF5nDjgM4MjQy
Download Telegram
Задача: 1267. Count Servers that Communicate
Сложность: medium

На двумерной плоскости имеется n точек с целочисленными координатами points[i] = [xi, yi]. Верните минимальное время в секундах для посещения всех точек в порядке, заданном точками. Вы можете перемещаться по следующим правилам: за 1 секунду вы можете либо: переместиться по вертикали на одну единицу, по горизонтали на одну единицу, либо по диагонали sqrt(2) единиц (другими словами, переместиться на одну единицу по вертикали и на одну единицу по горизонтали за 1 секунду). Вы должны посетить точки в том же порядке, в котором они появляются в массиве. Вы можете проходить через точки, которые появляются позже в порядке, но они не считаются за посещение.

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


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

1⃣Подсчитайте количество серверов в каждой строке и каждом столбце.

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

3⃣Подсчитайте и верните количество взаимодействующих серверов.

😎 Решение:
public class Solution {
public int countServers(int[][] grid) {
int[] rows = new int[grid.length];
int[] cols = new int[grid[0].length];

for (int i = 0; i < grid.length; i++) {
for (int j = 0; j < grid[0].length; j++) {
if (grid[i][j] == 1) {
rows[i]++;
cols[j]++;
}
}
}

int count = 0;
for (int i = 0; i < grid.length; i++) {
for (int j = 0; j < grid[0].length; j++) {
if (grid[i][j] == 1 && (rows[i] > 1 || cols[j] > 1)) {
count++;
}
}
}

return count;
}
}


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

Дано массив уникальных строк words, верните минимально возможные сокращения для каждого слова.

Правила сокращения строки следующие:
Первоначальное сокращение для каждого слова: первый символ, затем количество символов между первым и последним символом, затем последний символ.
Если более одного слова имеют одинаковое сокращение, выполните следующее:
Увеличьте префикс (символы в первой части) каждого из их сокращений на 1.
Например, начнем с слов ["abcdef", "abndef"], оба изначально сокращены как "a4f". Последовательность операций будет следующей: ["a4f", "a4f"] -> ["ab3f", "ab3f"] -> ["abc2f", "abn2f"].
Эта операция повторяется до тех пор, пока каждое сокращение не станет уникальным.
В конце, если сокращение не сделало слово короче, оставьте его в исходном виде.

Пример:
Input: words = ["like","god","internal","me","internet","interval","intension","face","intrusion"]
Output: ["l2e","god","internal","me","i6t","interval","inte4n","f2e","intr4n"]


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

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

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

3⃣ Возврат результата:
Верните окончательные сокращения для каждого слова, убедившись, что они минимально возможны и уникальны.

😎 Решение:
class Solution {
public List<String> wordsAbbreviation(List<String> words) {
int N = words.size();
String[] ans = new String[N];
int[] prefix = new int[N];

for (int i = 0; i < N; ++i)
ans[i] = abbrev(words.get(i), 0);

for (int i = 0; i < N; ++i) {
while (true) {
Set<Integer> dupes = new HashSet();
for (int j = i+1; j < N; ++j)
if (ans[i].equals(ans[j]))
dupes.add(j);

if (dupes.isEmpty()) break;
dupes.add(i);
for (int k: dupes)
ans[k] = abbrev(words.get(k), ++prefix[k]);
}
}

return Arrays.asList(ans);
}

public String abbrev(String word, int i) {
int N = word.length();
if (N - i <= 3) return word;
return word.substring(0, i+1) + (N - i - 2) + word.charAt(N-1);
}
}


Ставь 👍 и забирай 📚 Базу знаний
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-го предмета).
Верните любое решение, если существует более одного решения, и верните пустой список, если решения не существует.

Пример:
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]


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

1⃣Инициализация и создание графов:
Присвоить уникальные идентификаторы группам для элементов без группы.
Создать два графа: item_graph для элементов и group_graph для групп. Также создать два массива для учета входящих рёбер для элементов и групп.

2⃣Построение графов:
Пройти по массиву beforeItems и добавить зависимости между элементами в item_graph, увеличивая счётчик входящих рёбер.
Если элементы принадлежат разным группам, добавить зависимость между группами в group_graph, увеличивая счётчик входящих рёбер.

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

😎 Решение:
class Solution {
public int[] sortItems(int n, int m, int[] group, List<List<Integer>> beforeItems) {
int groupId = m;
for (int i = 0; i < n; i++) if (group[i] == -1) group[i] = groupId++;

Map<Integer, List<Integer>> itemGraph = new HashMap<>();
Map<Integer, List<Integer>> groupGraph = new HashMap<>();
int[] itemIndegree = new int[n], groupIndegree = new int[groupId];
for (int i = 0; i < n; i++) itemGraph.put(i, new ArrayList<>());
for (int i = 0; i < groupId; i++) groupGraph.put(i, new ArrayList<>());

for (int curr = 0; curr < n; curr++) {
for (int prev : beforeItems.get(curr)) {
itemGraph.get(prev).add(curr);
itemIndegree[curr]++;
if (group[curr] != group[prev]) {
groupGraph.get(group[prev]).add(group[curr]);
groupIndegree[group[curr]]++;
}
}
}

List<Integer> itemOrder = topologicalSort(itemGraph, itemIndegree);
List<Integer> groupOrder = topologicalSort(groupGraph, groupIndegree);
if (itemOrder.isEmpty() || groupOrder.isEmpty()) return new int[0];

Map<Integer, List<Integer>> orderedGroups = new HashMap<>();
for (int item : itemOrder) orderedGroups.computeIfAbsent(group[item], k -> new ArrayList<>()).add(item);

List<Integer> answerList = new ArrayList<>();
for (int groupIndex : groupOrder) answerList.addAll(orderedGroups.getOrDefault(groupIndex, new ArrayList<>()));

return answerList.stream().mapToInt(Integer::intValue).toArray();
}

private List<Integer> topologicalSort(Map<Integer, List<Integer>> graph, int[] indegree) {
List<Integer> visited = new ArrayList<>();
Stack<Integer> stack = new Stack<>();
for (int key : graph.keySet()) if (indegree[key] == 0) stack.add(key);

while (!stack.isEmpty()) {
int curr = stack.pop();
visited.add(curr);
for (int next : graph.get(curr)) if (--indegree[next] == 0) stack.add(next);
}

return visited.size() == graph.size() ? visited : new ArrayList<>();
}
}


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

"Даны две строки s и t. Верните количество различных подпоследовательностей строки s, которые равны строке t.

Тестовые примеры сгенерированы таким образом, что ответ укладывается в 32-битное целое число со знаком."

Пример:
Input: s = "rabbbit", t = "rabbit"
Output: 3
Explanation:
As shown below, there are 3 ways you can generate "rabbit" from s.
rabbbit
rabbbit
rabbbit


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

1⃣Определите функцию с названием recurse, которая принимает два целочисленных значения i и j. Первое значение представляет текущий обрабатываемый символ в строке S, а второе - текущий символ в строке T. Инициализируйте словарь под названием memo, который будет кэшировать результаты различных вызовов рекурсии.**

2⃣Проверьте базовый случай. Если одна из строк закончилась, возвращается 0 или 1 в зависимости от того, удалось ли обработать всю строку T или нет. Есть ещё один базовый случай, который следует рассмотреть. Если оставшаяся длина строки S меньше, чем у строки T, то сoвпадение невозможно. Если это обнаруживается, то рекурсия также обрезается и возвращается 0.**

3⃣Затем проверяем, существует ли текущая пара индексов в нашем словаре. Если да, то просто возвращаем сохранённое/кэшированное значение. Если нет, то продолжаем обычную обработку. Сравниваем символы s[i] и t[j]. Сохраняем результат вызова recurse(i + 1, j) в переменную. Как упоминалось ранее, результат этой рекурсии необходим, независимо от совпадения символов. Если символы совпадают, добавляем к переменной результат вызова recurse(i + 1, j + 1). Наконец, сохраняем значение этой переменной в словаре с ключом (i, j) и возвращаем это значение в качестве ответа.**

😎 Решение:
class Solution {
private HashMap<Pair<Integer, Integer>, Integer> memo;

private int recurse(String s, String t, int i, int j) {
int M = s.length();
int N = t.length();

if (i == M || j == N || M - i < N - j) {
return j == t.length() ? 1 : 0;
}

Pair<Integer, Integer> key = new Pair<Integer, Integer>(i, j);

if (this.memo.containsKey(key)) {
return this.memo.get(key);
}

int ans = this.recurse(s, t, i + 1, j);

if (s.charAt(i) == t.charAt(j)) {
ans += this.recurse(s, t, i + 1, j + 1);
}

this.memo.put(key, ans);
return ans;
}

public int numDistinct(String s, String t) {
this.memo = new HashMap<Pair<Integer, Integer>, Integer>();
return this.recurse(s, t, 0, 0);
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 83. Remove Duplicates from Sorted List
Сложность: easy

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

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


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

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

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

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

😎 Решение:
class Solution {
public ListNode deleteDuplicates(ListNode head) {
ListNode current = head;
while (current != null && current.next != null) {
if (current.next.val == current.val) {
current.next = current.next.next;
} else {
current = current.next;
}
}
return head;
}
}


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

Дан массив строк equations, представляющий отношения между переменными, где каждая строка equations[i] имеет длину 4 и принимает одну из двух форм: "xi==yi" или "xi!=yi". Здесь xi и yi - это строчные буквы (не обязательно разные), представляющие имена переменных из одной буквы.

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

Пример:
Input: equations = ["b==a","a==b"]
Output: true
Explanation: We could assign a = 1 and b = 1 to satisfy both equations.


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

1⃣Создание графа для уравнений "==":
Создайте структуру данных для объединения (Union-Find) для обработки уравнений равенства.
Пройдите через массив equations и для каждого уравнения типа "xi==yi" объедините соответствующие переменные.

2⃣Проверка уравнений "!=":
Пройдите через массив equations и для каждого уравнения типа "xi!=yi" проверьте, принадлежат ли переменные к одной и той же группе в структуре объединения. Если принадлежат, верните false.

3⃣Возврат результата:
Если после проверки всех уравнений "xi!=yi" не было обнаружено противоречий, верните true.

😎 Решение:
public class Solution {
public boolean equationsPossible(String[] equations) {
UnionFind uf = new UnionFind(26);

for (String eq : equations) {
if (eq.charAt(1) == '=') {
int x = eq.charAt(0) - 'a';
int y = eq.charAt(3) - 'a';
uf.unionSet(x, y);
}
}

for (String eq : equations) {
if (eq.charAt(1) == '!') {
int x = eq.charAt(0) - 'a';
int y = eq.charAt(3) - 'a';
if (uf.connected(x, y)) {
return false;
}
}
}

return true;
}

private class UnionFind {
private int[] parent;

public UnionFind(int n) {
parent = new int[n];
for (int i = 0; i < n; i++) {
parent[i] = i;
}
}

public int find(int x) {
if (parent[x] != x) {
parent[x] = find(parent[x]);
}
return parent[x];
}

public void unionSet(int x, int y) {
int rootX = find(x);
int rootY = find(y);
if (rootX != rootY) {
parent[rootX] = rootY;
}
}

public boolean connected(int x, int y) {
return find(x) == find(y);
}
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 713. Subarray Product Less Than K
Сложность: medium

Если задан массив целых чисел nums и целое число k, верните количество смежных подмассивов, в которых произведение всех элементов в подмассиве строго меньше k.

Пример:
Input: nums = [10,5,2,6], k = 100
Output: 8


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

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

2⃣Перемещайте правый указатель по массиву и умножайте текущий элемент на текущее произведение. Если произведение становится больше или равно k, перемещайте левый указатель, уменьшая произведение до тех пор, пока оно снова не станет меньше k.

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

😎 Решение:
public class Solution {
public int numSubarrayProductLessThanK(int[] nums, int k) {
if (k <= 1) return 0;
int product = 1, count = 0, left = 0;
for (int right = 0; right < nums.length; right++) {
product *= nums[right];
while (product >= k) {
product /= nums[left];
left++;
}
count += right - left + 1;
}
return count;
}
}


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

У вас есть одна шоколадка, состоящая из нескольких кусочков. Каждый кусочек имеет свою сладость, заданную массивом сладости. Вы хотите поделиться шоколадом со своими k друзьями, поэтому начинаете разрезать шоколадку на k + 1 кусочков с помощью k разрезов, каждый кусочек состоит из нескольких последовательных кусочков. Будучи щедрым, вы съедите кусочек с минимальной общей сладостью, а остальные кусочки отдадите своим друзьям. Найдите максимальную общую сладость кусочка, который вы можете получить, оптимально разрезав шоколадку.

Пример:
Input: sweetness = [1,2,3,4,5,6,7,8,9], k = 5
Output: 6


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

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

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

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

😎 Решение:
public class Solution {
public int maximizeSweetness(int[] sweetness, int k) {
int left = Arrays.stream(sweetness).min().getAsInt();
int right = Arrays.stream(sweetness).sum() / (k + 1);

while (left < right) {
int mid = (left + right + 1) / 2;
if (canDivide(sweetness, k, mid)) {
left = mid;
} else {
right = mid - 1;
}
}

return left;
}

private boolean canDivide(int[] sweetness, int k, int minSweetness) {
int currentSum = 0, cuts = 0;
for (int sweet : sweetness) {
currentSum += sweet;
if (currentSum >= minSweetness) {
cuts++;
currentSum = 0;
}
}
return cuts >= k + 1;
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
💊2
Задача: 947. Most Stones Removed with Same Row or Column
Сложность: medium

Учитывая массив stones длины n, где stones[i] = [xi, yi] представляет местоположение i-го камня, верните наибольшее возможное количество камней, которые могут быть удалены.

Пример:
Input: stones = [[0,0],[0,1],[1,0],[1,2],[2,1],[2,2]]
Output: 5


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

1⃣Представить каждую строку и столбец как узлы в графе.

2⃣Создать связи между узлами для камней, которые находятся в той же строке или столбце.
Использовать алгоритм поиска в глубину (DFS) или объединение-поиска (Union-Find), чтобы найти компоненты связности.

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

😎 Решение:
import java.util.*;

class Solution {
public int removeStones(int[][] stones) {
Map<Integer, Integer> parent = new HashMap<>();

int find(int x) {
if (!parent.containsKey(x)) {
parent.put(x, x);
}
if (parent.get(x) != x) {
parent.put(x, find(parent.get(x)));
}
return parent.get(x);
}

void union(int x, int y) {
parent.put(find(x), find(y));
}

for (int[] stone : stones) {
union(stone[0], ~stone[1]);
}

Set<Integer> uniqueRoots = new HashSet<>();
for (int key : parent.keySet()) {
uniqueRoots.add(find(key));
}

return stones.length - uniqueRoots.size();
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
💊1
Задача: 927. Three Equal Parts
Сложность: hard

Вам дан массив arr, состоящий только из нулей и единиц. Разделите массив на три непустые части так, чтобы все эти части представляли одно и то же двоичное значение. Если это возможно, верните любой [i, j] с i + 1 < j, такой что: arr[0], arr[1], ..., arr[i] - это первая часть, arr[i + 1], arr[i + 2], ...., arr[j - 1] - вторая часть, и arr[j], arr[j + 1], ..., arr[arr.length - 1] - третья часть. Все три части имеют одинаковые двоичные значения. Если это невозможно, верните [-1, -1]. Обратите внимание, что вся часть используется при рассмотрении того, какое двоичное значение она представляет. Например, [1,1,0] представляет 6 в десятичной системе, а не 3. Кроме того, допускаются ведущие нули, поэтому [0,1,1] и [1,1] представляют одно и то же значение.

Пример:
Input: arr = [1,0,1,0,1]
Output: [0,3]


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

1⃣Подсчитать количество единиц в массиве.

2⃣Если количество единиц не делится на три, вернуть [-1, -1].
Найти индексы начала каждой части, игнорируя ведущие нули.
Использовать эти индексы для проверки, могут ли три части быть одинаковыми.

3⃣Если три части одинаковы, вернуть соответствующие индексы, иначе вернуть [-1, -1].

😎 Решение:
class Solution {
public int[] threeEqualParts(int[] arr) {
int ones = 0;
for (int num : arr) {
ones += num;
}
if (ones % 3 != 0) return new int[]{-1, -1};
if (ones == 0) return new int[]{0, arr.length - 1};

int partOnes = ones / 3;
int first = 0, second = 0, third = 0, cnt = 0;
for (int i = 0; i < arr.length; i++) {
if (arr[i] == 1) {
if (cnt == 0) first = i;
else if (cnt == partOnes) second = i;
else if (cnt == 2 * partOnes) third = i;
cnt++;
}
}

while (third < arr.length && arr[first] == arr[second] && arr[first] == arr[third]) {
first++;
second++;
third++;
}

if (third == arr.length) return new int[]{first - 1, second};
return new int[]{-1, -1};
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
💊1
Задача: 609. Find Duplicate File in System
Сложность: medium

Получив список paths информации о каталоге, включающий путь к каталогу и все файлы с содержимым в этом каталоге, верните все дубликаты файлов в файловой системе по их путям. Вы можете вернуть ответ в любом порядке. Группа дубликатов состоит как минимум из двух файлов с одинаковым содержимым. Одна строка информации о каталоге во входном списке имеет следующий формат: "root/d1/d2/.../dm f1.txt(f1_content) f2.txt(f2_content) ... fn.txt(fn_content)" Это означает, что в каталоге "root/d1/d2/.../dm" имеется n файлов (f1.txt, f2.txt ... fn.txt) с содержимым (f1_content, f2_content ... fn_content) соответственно. Обратите внимание, что n >= 1 и m >= 0. Если m = 0, это означает, что каталог является только корневым. На выходе получается список групп дублирующихся путей к файлам. Для каждой группы он содержит все пути к файлам, которые имеют одинаковое содержимое. Путь к файлу - это строка, имеющая следующий формат: "каталог_путь/имя_файла.txt".

Пример:
Input: paths = ["root/a 1.txt(abcd) 2.txt(efgh)","root/c 3.txt(abcd)","root/c/d 4.txt(efgh)","root 4.txt(efgh)"]
Output: [["root/a/2.txt","root/c/d/4.txt","root/4.txt"],["root/a/1.txt","root/c/3.txt"]]


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

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

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

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

😎 Решение:
import java.util.*;

public class Solution {
public List<List<String>> findDuplicate(String[] paths) {
Map<String, List<String>> contentToFilePaths = new HashMap<>();

for (String path : paths) {
String[] parts = path.split(" ");
String root = parts[0];

for (int i = 1; i < parts.length; i++) {
String[] fileParts = parts[i].split("\\(");
String fileName = fileParts[0];
String content = fileParts[1].substring(0, fileParts[1].length() - 1);

String filePath = root + "/" + fileName;
contentToFilePaths.computeIfAbsent(content, k -> new ArrayList<>()).add(filePath);
}
}

List<List<String>> result = new ArrayList<>();
for (List<String> filePaths : contentToFilePaths.values()) {
if (filePaths.size() > 1) {
result.add(filePaths);
}
}

return result;
}
}


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

В английском языке есть понятие "корень", за которым может следовать какое-то другое слово, чтобы образовать другое более длинное слово - назовем это слово производным. Например, если за корнем "help" следует слово "ful", мы можем образовать производное "helpful". Дайте словарь, состоящий из множества корней, и предложение, состоящее из слов, разделенных пробелами, замените все производные в предложении на образующий их корень. Если производное может быть заменено более чем одним корнем, замените его корнем, имеющим наименьшую длину. Верните предложение после замены.

Пример:
Input: dictionary = ["cat","bat","rat"], sentence = "the cattle was rattled by the battery"
Output: "the cat was rat by the bat"


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

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

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

3⃣Замените слово найденным корнем и соберите обновленное предложение.

😎 Решение:
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class Solution {
public String replaceWords(List<String> roots, String sentence) {
Set<String> rootSet = new HashSet<>(roots);

StringBuilder result = new StringBuilder();
for (String word : sentence.split(" ")) {
String replacement = word;
for (int i = 1; i <= word.length(); i++) {
String prefix = word.substring(0, i);
if (rootSet.contains(prefix)) {
replacement = prefix;
break;
}
}
if (result.length() > 0) {
result.append(" ");
}
result.append(replacement);
}

return result.toString();
}
}


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

Бинарные часы имеют 4 светодиода сверху для представления часов (0-11) и 6 светодиодов снизу для представления минут (0-59). Каждый светодиод представляет ноль или единицу, при этом младший разряд находится справа.

Пример:
Input: turnedOn = 1
Output: ["0:01","0:02","0:04","0:08","0:16","0:32","1:00","2:00","4:00","8:00"]


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

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

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

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

😎 Решение:
import java.util.ArrayList;
import java.util.List;

class Solution {
public List<String> readBinaryWatch(int turnedOn) {
List<String> results = new ArrayList<>();
for (int h = 0; h < 12; h++) {
for (int m = 0; m < 60; m++) {
if (Integer.bitCount(h) + Integer.bitCount(m) == turnedOn) {
results.add(String.format("%d:%02d", h, m));
}
}
}
return results;
}
}


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

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

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

Вы всегда начинаете строить левый дочерний узел родителя сначала, если он существует.

Пример:
Input: s = "4(2(3)(1))(6(5))"
Output: [4,2,6,3,1,5]


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

1⃣ Извлечение числа:
Определите функцию getNumber, которая извлекает целое число из текущей строки, начиная с указанного индекса. Учтите знак числа, если он есть.

2⃣ Построение поддерева:
Определите рекурсивную функцию str2treeInternal, которая принимает строку и текущий индекс в качестве входных данных и возвращает пару: узел TreeNode и следующий индекс для обработки.
Внутри функции извлеките значение для корневого узла текущего поддерева, создайте узел, а затем рекурсивно постройте левое и правое поддеревья, если они существуют.

3⃣ Основная функция:
Определите основную функцию str2tree, которая вызывает рекурсивную функцию str2treeInternal и возвращает построенное дерево.

😎 Решение:
class Solution {
public TreeNode str2tree(String s) {
return this.str2treeInternal(s, 0).getKey();
}

public Pair<Integer, Integer> getNumber(String s, int index) {

boolean isNegative = false;

if (s.charAt(index) == '-') {
isNegative = true;
index++;
}

int number = 0;
while (index < s.length() && Character.isDigit(s.charAt(index))) {
number = number * 10 + (s.charAt(index) - '0');
index++;
}

return new Pair<Integer, Integer>(isNegative ? -number : number, index);
}

public Pair<TreeNode, Integer> str2treeInternal(String s, int index) {

if (index == s.length()) {
return new Pair<TreeNode, Integer>(null, index);
}

Pair<Integer, Integer> numberData = this.getNumber(s, index);
int value = numberData.getKey();
index = numberData.getValue();

TreeNode node = new TreeNode(value);
Pair<TreeNode, Integer> data;

if (index < s.length() && s.charAt(index) == '(') {
data = this.str2treeInternal(s, index + 1);
node.left = data.getKey();
index = data.getValue();
}

if (node.left != null && index < s.length() && s.charAt(index) == '(') {
data = this.str2treeInternal(s, index + 1);
node.right = data.getKey();
index = data.getValue();
}

return new Pair<TreeNode, Integer>(node, index < s.length() && s.charAt(index) == ')' ? index + 1 : index);
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
💊1
Задача: 688. Knight Probability in Chessboard
Сложность: medium

На шахматной доске размером n x n конь начинает в клетке (row, column) и пытается сделать ровно k ходов. Строки и столбцы нумеруются с 0, так что верхняя левая клетка — это (0, 0), а нижняя правая — (n - 1, n - 1).

Шахматный конь имеет восемь возможных ходов, как показано ниже. Каждый ход — это два поля в кардинальном направлении, затем одно поле в ортогональном направлении.

Каждый раз, когда конь делает ход, он случайным образом выбирает один из восьми возможных ходов (даже если этот ход выведет его за пределы шахматной доски) и перемещается туда.

Конь продолжает двигаться, пока не сделает ровно k ходов или не выйдет за пределы доски.

Верните вероятность того, что конь останется на доске после того, как он завершит свои ходы.

Пример:
Input: n = 3, k = 2, row = 0, column = 0
Output: 0.06250
Explanation: There are two moves (to (1,2), (2,1)) that will keep the knight on the board.
From each of those positions, there are also two moves that will keep the knight on the board.
The total probability the knight stays on the board is 0.0625.


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

1⃣Определите возможные направления для ходов коня в directions. Инициализируйте таблицу динамического программирования dp нулями. Установите dp[0][row][column] равным 1, что представляет начальную позицию коня.

2⃣Итерация по ходам от 1 до k. Итерация по строкам от 0 до n−1. Итерация по столбцам от 0 до n−1. Итерация по возможным направлениям: вычислите i' как i минус вертикальный компонент направления. Вычислите j' как j минус горизонтальный компонент направления. Проверьте, находятся ли i' и j' в диапазоне [0, n−1]. Если находятся, добавьте (1/8) * dp[moves−1][i'][j'] к dp[moves][i][j].

3⃣Вычислите общую вероятность, суммируя все значения в dp[k]. Верните общую вероятность.

😎 Решение:
class Solution {
public double knightProbability(int n, int k, int row, int column) {
int[][] directions = {{1, 2}, {1, -2}, {-1, 2}, {-1, -2}, {2, 1}, {2, -1}, {-2, 1}, {-2, -1}};
double[][][] dp = new double[k + 1][n][n];
dp[0][row][column] = 1;

for (int moves = 1; moves <= k; moves++) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
for (int[] direction : directions) {
int prevI = i - direction[0];
int prevJ = j - direction[1];
if (prevI >= 0 && prevI < n && prevJ >= 0 && prevJ < n) {
dp[moves][i][j] += dp[moves - 1][prevI][prevJ] / 8;
}
}
}
}
}

double totalProbability = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
totalProbability += dp[k][i][j];
}
}

return totalProbability;
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Задача: 472. Concatenated Words
Сложность: hard

Дан массив строк words (без дубликатов). Верните все составные слова из данного списка слов.

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

Пример:
Input: words = ["cat","cats","catsdogcats","dog","dogcatsdog","hippopotamuses","rat","ratcatdogcat"]
Output: ["catsdogcats","dogcatsdog","ratcatdogcat"]
Explanation: "catsdogcats" can be concatenated by "cats", "dog" and "cats";
"dogcatsdog" can be concatenated by "dog", "cats" and "dog";
"ratcatdogcat" can be concatenated by "rat", "cat", "dog" and "cat".


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

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

2⃣Использовать поиск в глубину (DFS) для проверки, можно ли достигнуть узел с индексом word.length от узла с индексом 0 в графе.

3⃣Если узел word.length достижим от узла 0, добавить слово в ответ.

😎 Решение:
import java.util.*;

public class Solution {
private boolean dfs(String word, int length, boolean[] visited, Set<String> dictionary) {
if (length == word.length()) {
return true;
}
if (visited[length]) {
return false;
}
visited[length] = true;
for (int i = word.length() - (length == 0 ? 1 : 0); i > length; --i) {
if (dictionary.contains(word.substring(length, i)) && dfs(word, i, visited, dictionary)) {
return true;
}
}
return false;
}

public List<String> findAllConcatenatedWordsInADict(String[] words) {
Set<String> dictionary = new HashSet<>(Arrays.asList(words));
List<String> answer = new ArrayList<>();
for (String word : words) {
boolean[] visited = new boolean[word.length()];
if (dfs(word, 0, visited, dictionary)) {
answer.add(word);
}
}
return answer;
}
}


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

Вам дана целочисленная матричная сетка m x n и три целых числа row, col и color. Каждое значение в сетке представляет собой цвет квадрата сетки в данном месте. Два квадрата называются смежными, если они находятся рядом друг с другом в любом из 4 направлений. Два квадрата принадлежат одному связанному компоненту, если они имеют одинаковый цвет и являются смежными.

Граница связанного компонента - это все квадраты в связанном компоненте, которые либо смежны (по крайней мере) с квадратом, не входящим в компонент, либо находятся на границе сетки (в первой или последней строке или столбце). Вы должны окрасить границу связанного компонента, содержащего квадрат grid[row][col], в цвет. Верните конечную сетку.

Пример:
Input: grid = [[1,1],[1,2]], row = 0, col = 0, color = 3
Output: [[3,3],[3,2]]


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

1⃣Поиск связанного компонента:
Используйте поиск в глубину (DFS) или поиск в ширину (BFS), чтобы найти все клетки, принадлежащие связанному компоненту, содержащему клетку grid[row][col].
Запомните все клетки, которые принадлежат этому компоненту.

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

3⃣Окрашивание границы:
Измените цвет всех клеток, являющихся границами, на заданный цвет.

😎 Решение:
public class Solution {
public int[][] colorBorder(int[][] grid, int row, int col, int color) {
int m = grid.length, n = grid[0].length;
int originalColor = grid[row][col];
boolean[][] visited = new boolean[m][n];
List<int[]> borders = new ArrayList<>();

dfs(grid, row, col, originalColor, visited, borders);

for (int[] border : borders) {
grid[border[0]][border[1]] = color;
}

return grid;
}

private void dfs(int[][] grid, int r, int c, int color, boolean[][] visited, List<int[]> borders) {
int m = grid.length, n = grid[0].length;
visited[r][c] = true;
boolean isBorder = false;

for (int[] dir : new int[][]{{-1, 0}, {1, 0}, {0, -1}, {0, 1}}) {
int nr = r + dir[0], nc = c + dir[1];
if (nr >= 0 && nr < m && nc >= 0 && nc < n) {
if (!visited[nr][nc]) {
if (grid[nr][nc] == color) {
dfs(grid, nr, nc, color, visited, borders);
} else {
isBorder = true;
}
}
} else {
isBorder = true;
}
}

if (isBorder || r == 0 || r == m - 1 || c == 0 || c == n - 1) {
borders.add(new int[]{r, c});
}
}
}


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

Диаметр дерева - это количество ребер в самом длинном пути в этом дереве. Имеется неориентированное дерево из n узлов, помеченных от 0 до n - 1. Вам дан двумерный массив edges, где edges.length == n - 1 и edges[i] = [ai, bi] означает, что между узлами ai и bi в дереве есть неориентированное ребро. Верните диаметр дерева.

Пример:
Input: edges = [[0,1],[0,2]]
Output: 2


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

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

2⃣Поиск самой удаленной вершины (DFS1):
Запускаем DFS от произвольной вершины (например, 0) для нахождения самой удаленной вершины от нее.

3⃣Поиск диаметра (DFS2):
Запускаем DFS от найденной на предыдущем шаге самой удаленной вершины и находим самую удаленную вершину от нее. Это расстояние и будет диаметром дерева.reset(playerId):
Устанавливаем счет игрока в 0.

😎 Решение:
import java.util.*;

public class Solution {
public int treeDiameter(int[][] edges) {
if (edges.length == 0) return 0;

Map<Integer, List<Integer>> graph = new HashMap<>();
for (int[] edge : edges) {
graph.computeIfAbsent(edge[0], k -> new ArrayList<>()).add(edge[1]);
graph.computeIfAbsent(edge[1], k -> new ArrayList<>()).add(edge[0]);
}

int[] farthestNode = new int[1];

int dfs(int node, int parent) {
int maxDepth = 0;
for (int neighbor : graph.get(node)) {
if (neighbor != parent) {
int depth = dfs(neighbor, node);
if (depth + 1 > maxDepth) {
maxDepth = depth + 1;
farthestNode[0] = neighbor;
}
}
}
return maxDepth;
}

dfs(0, -1);
int startNode = farthestNode[0];

dfs(startNode, -1);
return dfs(farthestNode[0], -1);
}
}


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

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

Пример:
Input: img = [[1,1,1],[1,0,1],[1,1,1]]
Output: [[0,0,0],[0,0,0],[0,0,0]]
Explanation:
For the points (0,0), (0,2), (2,0), (2,2): floor(3/4) = floor(0.75) = 0
For the points (0,1), (1,0), (1,2), (2,1): floor(5/6) = floor(0.83333333) = 0
For the point (1,1): floor(8/9) = floor(0.88888889) = 0


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

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

2⃣Обработка каждой ячейки:
Для каждой ячейки исходной матрицы найдите всех её соседей (включая саму ячейку).
Вычислите среднее значение этих ячеек и сохраните его в соответствующей ячейке результирующей матрицы.

3⃣Возврат результата:
Верните результирующую матрицу после применения сглаживания ко всем ячейкам.

😎 Решение:
public class Solution {
public int[][] imageSmoother(int[][] img) {
int m = img.length, n = img[0].length;
int[][] result = new int[m][n];

for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
int count = 0, total = 0;
for (int ni = Math.max(0, i - 1); ni <= Math.min(m - 1, i + 1); ni++) {
for (int nj = Math.max(0, j - 1); nj <= Math.min(n - 1, j + 1); nj++) {
total += img[ni][nj];
count++;
}
}
result[i][j] = total / count;
}
}

return result;
}
}


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

Дан список оценок различных студентов, items, где items[i] = [IDi, scorei] представляет собой одну оценку студента с идентификатором IDi. Вычислите среднее значение пяти лучших оценок каждого студента.

Верните ответ в виде массива пар result, где result[j] = [IDj, topFiveAveragej] представляет студента с идентификатором IDj и его среднее значение пяти лучших оценок. Отсортируйте result по IDj в порядке возрастания.

Среднее значение пяти лучших оценок студента вычисляется путем сложения его пяти лучших оценок и деления на 5 с использованием целочисленного деления.

Пример:
Input: s = "{a,b}c{d,e}f"
Output: ["acdf","acef","bcdf","bcef"]


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

1⃣Вызовите функцию findAllWords(String, Integer) с данной строкой s и значением startPos равным 0. startPos представляет текущую позицию в строке s. Если строка, которую нужно рассмотреть, пуста (startPos == s.length()), верните список, содержащий пустую строку.

2⃣Вызовите функцию storeFirstOptions с строкой s, целым числом startPos и пустым списком firstOptions. Найдите набор символов, начиная с позиции startPos, и сохраните их в списке firstOptions. Это может быть один символ или все символы между скобками. Отсортируйте список firstOptions. Верните обновленное значение startPos, которое теперь указывает на первый индекс следующей группы символов в строке s, которую мы будем рассматривать. Сохраните это значение в переменной remStringStartPos. Сделайте рекурсивный вызов функции findAllWords(String, Integer) с строкой s и remStringStartPos. Сохраните возвращенный список слов в переменной wordsWithRemString.

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

😎 Решение:
class Solution {
private int storeFirstOptions(String s, int startPos, List<Character> firstOptions) {
if (s.charAt(startPos) != '{') {
firstOptions.add(s.charAt(startPos));
} else {
startPos++;
while (s.charAt(startPos) != '}') {
if (Character.isLowerCase(s.charAt(startPos))) {
firstOptions.add(s.charAt(startPos));
}
startPos++;
}
Collections.sort(firstOptions);
}
return startPos + 1;
}

private List<String> findAllWords(String s, int startPos) {
if (startPos == s.length()) {
return Arrays.asList("");
}

List<Character> firstOptions = new ArrayList<>();
int remStringStartPos = storeFirstOptions(s, startPos, firstOptions);
List<String> wordsWithRemString = findAllWords(s, remStringStartPos);

List<String> expandedWords = new ArrayList<>();
for (char c : firstOptions) {
for (String word : wordsWithRemString) {
expandedWords.add(c + word);
}
}
return expandedWords;
}

public List<String> expand(String s) {
return findAllWords(s, 0);
}
}


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