C/C++ | LeetCode
3.36K subscribers
159 photos
1.11K links
Cайт easyoffer.ru
Реклама @easyoffer_adv
ВП @easyoffer_vp

Тесты t.iss.one/+zYofcX2VLTM3MGMy
Вопросы собесов t.iss.one/+BTbqlW1VbIFmYmVi
Вакансии t.iss.one/+za2mJYs4riAzMzFi
Download Telegram
Задача: 1323. Maximum 69 Number
Сложность: easy

Дано положительное целое число num, состоящее только из цифр 6 и 9.

Верните максимальное число, которое можно получить, изменив не более одной цифры (6 становится 9, а 9 становится 6).

Пример:
Input: num = 9669
Output: 9969
Explanation:
Changing the first digit results in 6669.
Changing the second digit results in 9969.
Changing the third digit results in 9699.
Changing the fourth digit results in 9666.
The maximum number is 9969.


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

1⃣Преобразуйте входное целое число num в итерируемый и изменяемый объект num_obj.

2⃣Пройдитесь по num_obj и, если найдете цифру 6, замените её на 9 и прекратите итерацию.

3⃣Верните целое число, преобразованное из измененного num_obj.

😎 Решение:
class Solution {
public:
int maximum69Number (int num) {
string numStr = to_string(num);
for (char &c : numStr) {
if (c == '6') {
c = '9';
break;
}
}
return stoi(numStr);
}
};


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

Реализуйте базовый калькулятор для вычисления простого строкового выражения.

Строка выражения содержит только неотрицательные целые числа, операторы '+', '-', '*', '/' и открывающие '(' и закрывающие скобки ')'. Целочисленное деление должно округляться к нулю.

Предполагается, что данное выражение всегда корректно. Все промежуточные результаты будут находиться в диапазоне [-2^31, 2^31 - 1].

Примечание: нельзя использовать встроенные функции для вычисления строк как математических выражений, такие как eval().

Пример:
Input: s = "1+1"
Output: 2


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

1⃣Определите вспомогательную функцию evaluate, которая принимает оператор и числовые аргументы. Заметьте, что эта функция идентична той, что представлена в подходе "Basic Calculator II". Инициализируйте несколько переменных: стек для хранения промежуточных результатов, curr для отслеживания текущего числа, которое мы строим, и previousOperator для отслеживания предыдущего оператора. Добавьте в строку s случайный символ, который не будет появляться во входных данных, например "@".

2⃣Итерация по входным данным. Для каждого символа c: если c является цифрой, добавьте его к curr. В противном случае, если c == '(', мы начинаем вычисление нового изолированного выражения. В этом случае сохраните previousOperator в стек и установите previousOperator = "+".

3⃣Если c является оператором, то необходимо вычислить значение curr. Используйте функцию evaluate, чтобы применить previousOperator к curr, и добавьте результат в стек. Затем сбросьте curr до нуля и обновите previousOperator = c. Если c == ')', это означает, что мы находимся в конце изолированного выражения и должны полностью его вычислить. Извлекайте из стека до тех пор, пока не достигнете оператора, суммируя все извлеченные числа в curr. Как только достигнете оператора, обновите previousOperator = stack.pop(). Верните сумму всех чисел в стеке.

😎 Решение:
class Solution {
public:
string evaluate(char operator, string first, string second) {
int x = stoi(first);
int y = stoi(second);
int res = 0;

if (operator == '+') {
res = x;
} else if (operator == '-') {
res = -x;
} else if (operator == '*') {
res = x * y;
} else {
res = x / y;
}

return to_string(res);
}

int calculate(string s) {
stack<string> stack;
string curr = "";
char previousOperator = '+';
s += "@";
unordered_set<string> operators = {"+", "-", "*", "/"};

for (char c: s) {
if (isdigit(c)) {
curr += c;
} else if (c == '(') {
stack.push(string(1, previousOperator));
previousOperator = '+';
} else {
if (previousOperator == '*' || previousOperator == '/') {
stack.push(evaluate(previousOperator, stack.top(), curr));
stack.pop();
} else {
stack.push(evaluate(previousOperator, curr, "0"));
}

curr = "";
previousOperator = c;
if (c == ')') {
int currentTerm = 0;
while (operators.find(stack.top()) == operators.end()) {
currentTerm += stoi(stack.top());
stack.pop();
}

curr = to_string(currentTerm);
previousOperator = stack.top()[0];
stack.pop();
}
}
}

int ans = 0;
while (!stack.empty()) {
ans += stoi(stack.top());
stack.pop();
}

return ans;
}
};


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 302. Smallest Rectangle Enclosing Black Pixels
Сложность: hard

Вам дана бинарная матрица размером m x n, где 0 представляет собой белый пиксель, а 1 представляет собой черный пиксель.
Черные пиксели соединены (то есть существует только одна черная область). Пиксели соединены по горизонтали и вертикали.
Даны два целых числа x и y, которые представляют местоположение одного из черных пикселей. Верните площадь наименьшего (выравненного по осям) прямоугольника, который охватывает все черные пиксели.
Вы должны написать алгоритм со сложностью менее O(mn).

Пример:
Input: image = [["0","0","1","0"],["0","1","1","0"],["0","1","0","0"]], x = 0, y = 2
Output: 6

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

1⃣Инициализация границ прямоугольника: Инициализируйте переменные left, right, top и bottom. left и top задаются значениями координат (x, y), right и bottom - значениями x + 1 и y + 1 соответственно.

2⃣Обход всех пикселей: Пройдите по всем координатам (x, y) матрицы. Если текущий пиксель является черным (image[x][y] == 1), обновите границы прямоугольника:
left = min(left, x)
right = max(right, x + 1)
top = min(top, y)
bottom = max(bottom, y + 1)

3⃣Вычисление и возврат площади: После завершения обхода матрицы, верните площадь прямоугольника, используя формулу (right - left) * (bottom - top).

😎 Решение:
class Solution {
public:
int minArea(vector<vector<char>>& image, int x, int y) {
int m = image.size(), n = image[0].size();
int left = searchColumns(image, 0, y, 0, m, true);
int right = searchColumns(image, y + 1, n, 0, m, false);
int top = searchRows(image, 0, x, left, right, true);
int bottom = searchRows(image, x + 1, m, left, right, false);
return (right - left) * (bottom - top);
}

private:
int searchColumns(vector<vector<char>>& image, int i, int j, int top, int bottom, bool opt) {
while (i != j) {
int k = (i + j) / 2;
int t = top;
while (t < bottom && image[t][k] == '0') t++;
if ((t < bottom) == opt) {
j = k;
} else {
i = k + 1;
}
}
return i;
}

int searchRows(vector<vector<char>>& image, int i, int j, int left, int right, bool opt) {
while (i != j) {
int k = (i + j) / 2;
int l = left;
while (l < right && image[k][l] == '0') l++;
if ((l < right) == opt) {
j = k;
} else {
i = k + 1;
}
}
return i;
}
};


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Forwarded from easyoffer
🎉 easyoffer 2.0 — релиз уже в этом месяце!

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

В честь запуска мы готовим ограниченную акцию:

Первые 500 покупателей получат:
🚀 PRO тариф на 1 год с 50% скидкой

Что нужно сделать:

🔔 Подпишитесь на этот Telegram-канал, чтобы первыми узнать о старте релиза. Сообщение появится в нем раньше, чем где-либо еще — вы успеете попасть в число первых 500 и получить максимальную выгоду. 🎁 А еще только для подписчиков канала ценный бонус в подарок к PRO тарифу.

📅 Официальный запуск — уже совсем скоро.
Следите за новостями и не пропустите старт!
Задача: 115. Distinct Subsequences
Сложность: hard

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

Пример:
Input: s = "rabbbit", t = "rabbit" Output: 3
Explanation:
Из строки s = "rabbbit" можно получить "rabbit" тремя разными способами, выбирая разные b на позиции 2, 3 и 4.

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

1⃣Реализуем рекурсивную функцию recurse(i, j), где i — текущий индекс в строке s, а j — в строке t. Используем хэш-таблицу memo для кэширования результатов recurse(i, j).

2⃣Базовые случаи:
Если j == t.length() — вся строка t успешно сопоставлена, возвращаем 1.
Если i == s.length() — строка s закончилась до завершения t, возвращаем 0.

3⃣Если s[i] == t[j], то у нас два варианта:
использовать символ s[i] → recurse(i+1, j+1)
пропустить символ s[i] → recurse(i+1, j)
Если s[i] != t[j], продолжаем только с recurse(i+1, j). Результат сохраняем в memo и возвращаем.

😎 Решение:
class Solution {
private:
unordered_map<string, int> memo;

public:
int numDistinct(string s, string t) {
if (s.size() < t.size()) return 0;
if (s == t || t.empty()) return 1;

string key = to_string(s.size()) + "," + to_string(t.size());
if (memo.find(key) != memo.end()) return memo[key];

int N = s.size(), M = t.size();
int ans = numDistinct(s.substr(0, N - 1), t);

if (s[N - 1] == t[M - 1])
ans += numDistinct(s.substr(0, N - 1), t.substr(0, M - 1));

memo[key] = ans;
return ans;
}
};


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

Вам дан массив пар переменных equations и массив вещественных чисел values, где equations[i] = [Ai, Bi] и values[i] представляют уравнение Ai / Bi = values[i]. Каждая Ai или Bi - это строка, представляющая одну переменную. Вам также даны некоторые запросы, где queries[j] = [Cj, Dj] представляет j-й запрос, в котором вы должны найти ответ для Cj / Dj = ?. Верните ответы на все запросы. Если ни один ответ не может быть определен, верните -1.0. Замечание: входные данные всегда действительны. Можно предположить, что вычисление запросов не приведет к делению на ноль и что противоречия нет. Примечание: Переменные, которые не встречаются в списке уравнений, являются неопределенными, поэтому для них ответ не может быть определен.

Пример:
Input: equations = [["a","b"],["b","c"]], values = [2.0,3.0], queries = [["a","c"],["b","a"],["a","e"],["a","a"],["x","x"]]
Output: [6.00000,0.50000,-1.00000,1.00000,-1.00000]

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

1⃣Создание графа:
Представьте каждую переменную как узел в графе.
Используйте уравнения для создания ребер между узлами, где каждое ребро имеет вес, равный значению уравнения (Ai / Bi = values[i]).
Создайте также обратные ребра с обратным весом (Bi / Ai = 1 / values[i]).

2⃣Поиск пути:
Для каждого запроса используйте поиск в глубину (DFS) или поиск в ширину (BFS) для поиска пути от Cj до Dj.
Если путь найден, вычислите произведение весов вдоль пути, чтобы найти значение Cj / Dj.
Если путь не найден, верните -1.0.

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

😎 Решение:
using namespace std;

class Solution {
public:
vector<double> calcEquation(vector<vector<string>>& equations, vector<double>& values, vector<vector<string>>& queries) {
unordered_map<string, unordered_map<string, double>> graph;

for (int i = 0; i < equations.size(); ++i) {
string A = equations[i][0], B = equations[i][1];
double value = values[i];
graph[A][B] = value;
graph[B][A] = 1.0 / value;
}

vector<double> results;
for (auto& query : queries) {
string C = query[0], D = query[1];
if (!graph.count(C) || !graph.count(D)) {
results.push_back(-1.0);
} else {
results.push_back(bfs(C, D, graph));
}
}
return results;
}

private:
double bfs(const string& start, const string& end, unordered_map<string, unordered_map<string, double>>& graph) {
queue<pair<string, double>> q;
q.push({start, 1.0});
unordered_set<string> visited;

while (!q.empty()) {
auto [current, product] = q.front(); q.pop();
if (current == end) return product;
visited.insert(current);
for (auto& [neighbor, value] : graph[current]) {
if (!visited.count(neighbor)) {
q.push({neighbor, product * value});
}
}
}
return -1.0;
}
};


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

Напишите программу для подсчета количества дней между двумя датами.

Даты даны в виде строк в формате YYYY-MM-DD, как показано в примерах.

Пример:
Input: date1 = "2019-06-29", date2 = "2019-06-30"
Output: 1


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

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

2⃣Вычисление разницы в днях
Вычислите разницу между двумя объектами дат в днях.

3⃣Возвращение результата
Верните абсолютное значение разницы в днях для получения положительного числа.

😎 Решение:
#include <iostream>
#include <string>
#include <cmath>
#include <ctime>

class Solution {
public:
int daysBetweenDates(std::string date1, std::string date2) {
std::tm tm1 = {}, tm2 = {};
strptime(date1.c_str(), "%Y-%m-%d", &tm1);
strptime(date2.c_str(), "%Y-%m-%d", &tm2);
std::time_t time1 = std::mktime(&tm1);
std::time_t time2 = std::mktime(&tm2);
return std::abs((time2 - time1) / (60 * 60 * 24));
}
};


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Задача: 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⃣Подсчитайте количество подмассивов с текущим правым указателем, добавив к общему количеству допустимых подмассивов разницу между правым и левым указателями.

😎 Решение:
int numSubarrayProductLessThanK(vector<int>& nums, int k) {
if (k <= 1) return 0;
int product = 1, count = 0, left = 0;
for (int right = 0; right < nums.size(); 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
👍1
Задача: 1011. Capacity To Ship Packages Within D Days
Сложность: medium

На конвейерной ленте находятся пакеты, которые должны быть отправлены из одного порта в другой в течение нескольких дней. i-й пакет на конвейерной ленте имеет массу weights[i]. Каждый день мы загружаем корабль пакетами на конвейерной ленте (в порядке, заданном весами). Мы не можем загрузить больше груза, чем максимальная грузоподъемность корабля. Верните наименьшую грузоподъемность корабля, при которой все посылки на конвейере будут отправлены в течение нескольких дней.

Пример:
Input: weights = [1,2,3,4,5,6,7,8,9,10], days = 5
Output: 15


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

1⃣Определение диапазона возможных ответов:
Минимальная грузоподъемность должна быть не меньше максимального веса одного пакета (чтобы хотя бы один пакет можно было загрузить).
Максимальная грузоподъемность - это сумма всех весов (если все пакеты будут отправлены за один день).

2⃣Использование бинарного поиска:
Примените бинарный поиск в диапазоне от минимальной до максимальной грузоподъемности, чтобы найти наименьшую грузоподъемность, при которой все пакеты можно отправить за заданное количество дней.

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

😎 Решение:
class Solution {
public:
int shipWithinDays(vector<int>& weights, int D) {
auto canShipInDays = [&](int capacity) {
int days = 1, total = 0;
for (int weight : weights) {
if (total + weight > capacity) {
days++;
total = 0;
}
total += weight;
}
return days <= D;
};

int left = *max_element(weights.begin(), weights.end());
int right = accumulate(weights.begin(), weights.end(), 0);

while (left < right) {
int mid = (left + right) / 2;
if (canShipInDays(mid)) {
right = mid;
} else {
left = mid + 1;
}
}

return left;
}
};


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

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

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


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

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

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

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

😎 Решение:
class Solution {
public:
int minChangesToMakePalindrome(string s, int k) {
int n = s.length();

vector<vector<int>> dp1(n, vector<int>(n, 0));
for (int length = 1; length <= n; length++) {
for (int i = 0; i <= n - length; i++) {
int j = i + length - 1;
dp1[i][j] = minChangeToPalindrome(s, i, j);
}
}

vector<vector<int>> dp2(n + 1, vector<int>(k + 1, INT_MAX));
dp2[0][0] = 0;

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

return dp2[n][k];
}

private:
int minChangeToPalindrome(const string& s, int i, int j) {
int changes = 0;
while (i < j) {
if (s[i] != s[j]) {
changes++;
}
i++;
j--;
}
return changes;
}
};


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

Дан массив различных целых чисел arr, отсортированный в порядке возрастания. Верните наименьший индекс i, который удовлетворяет условию arr[i] == i. Если такого индекса нет, верните -1.

Пример:
Input: arr = [-10,-5,0,3,7]
Output: 3
Explanation: For the given array, arr[0] = -10, arr[1] = -5, arr[2] = 0, arr[3] = 3, thus the output is 3.


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

1⃣Инициализируйте значение left как 0, right как N - 1 и answer как -1.

2⃣Пока размер области поиска не равен нулю, то есть left <= right, выполните следующие шаги: найдите mid как mid = (left + right) / 2. Сравните arr[mid] и mid: если arr[mid] = mid, сохраните mid в answer и перейдите в левую часть, изменив right на mid - 1; если arr[mid] < mid, перейдите в правую часть, изменив left на mid + 1; если arr[mid] > mid, перейдите в левую часть, изменив right на mid - 1.

3⃣Верните answer.

😎 Решение:
class Solution {
public:
int fixedPoint(vector<int>& arr) {
int left = 0, right = arr.size() - 1;
int answer = -1;

while (left <= right) {
int mid = (left + right) / 2;

if (arr[mid] == mid) {
answer = mid;
right = mid - 1;
} else if (arr[mid] < mid) {
left = mid + 1;
} else {
right = mid - 1;
}
}

return answer;
}
};


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

Учитывая целое число n, верните список всех возможных полных бинарных деревьев с узлами. Каждый узел каждого дерева в ответе должен иметь Node.val == 0. Каждый элемент ответа является корневым узлом одного возможного дерева. Вы можете вернуть конечный список деревьев в любом порядке. Полное бинарное дерево - это бинарное дерево, в котором каждый узел имеет ровно 0 или 2 дочерних.

Пример:
Input: n = 7
Output: [[0,0,0,null,null,0,0,null,null,0,0],[0,0,0,null,null,0,0,0,0],[0,0,0,0,0,0,0],[0,0,0,0,0,null,null,null,null,0,0],[0,0,0,0,0,null,null,0,0]]


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

1⃣Если n четное, вернуть пустой список, так как полное бинарное дерево не может иметь четное количество узлов.

2⃣Если n == 1, вернуть дерево с одним узлом. Для всех возможных значений i от 1 до n-1 с шагом 2: Создать левое поддерево с i узлами. Создать правое поддерево с n-1-i узлами. Объединить все комбинации левого и правого поддеревьев с новым корнем, добавив их в список результатов.

3⃣Вернуть список результатов.

😎 Решение:
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(0), left(nullptr), right(nullptr) {}
};

class Solution {
public:
vector<TreeNode*> allPossibleFBT(int n) {
if (n % 2 == 0) return {};
if (n == 1) return {new TreeNode(0)};

vector<TreeNode*> result;
for (int i = 1; i < n; i += 2) {
vector<TreeNode*> leftTrees = allPossibleFBT(i);
vector<TreeNode*> rightTrees = allPossibleFBT(n - 1 - i);
for (auto left : leftTrees) {
for (auto right : rightTrees) {
TreeNode* root = new TreeNode(0);
root.left = left;
root.right = right;
result.push_back(root);
}
}
}
return result;
}
};


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

Даны две строки s и t, верните true, если они равны после ввода в пустые текстовые редакторы. Символ '#' означает клавишу backspace.

Обратите внимание, что после нажатия backspace на пустом тексте, текст останется пустым.

Пример:
Input: s = "ab#c", t = "ad#c"
Output: true
Explanation: Both s and t become "ac".


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

1⃣Пройдите по строкам s и t с конца, учитывая символы '#' как backspace и пропуская соответствующие символы.

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

3⃣Если все соответствующие символы совпадают и строки эквивалентны после всех backspace операций, верните true; в противном случае верните false.

😎 Решение:
class Solution {
public:
bool backspaceCompare(string S, string T) {
int i = S.length() - 1, j = T.length() - 1;
int skipS = 0, skipT = 0;

while (i >= 0 || j >= 0) {
while (i >= 0) {
if (S[i] == '#') { skipS++; i--; }
else if (skipS > 0) { skipS--; i--; }
else break;
}
while (j >= 0) {
if (T[j] == '#') { skipT++; j--; }
else if (skipT > 0) { skipT--; j--; }
else break;
}
if (i >= 0 && j >= 0 && S[i] != T[j]) {
return false;
}
if ((i >= 0) != (j >= 0)) {
return false;
}
i--; j--;
}
return true;
}
};


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

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

Сотни представляют глубину d этого узла, где 1 <= d <= 4.
Десятки представляют позицию p этого узла на уровне, которому он принадлежит, где 1 <= p <= 8. Позиция соответствует позиции в полном бинарном дереве.
Единицы представляют значение v этого узла, где 0 <= v <= 9.
Дан массив возрастающих трехзначных чисел nums, представляющих бинарное дерево с глубиной меньше 5. Верните сумму всех путей от корня к листьям.

Гарантируется, что данный массив представляет собой валидное связанное бинарное дерево.

Пример:
Input: nums = [113,215,221]
Output: 12
Explanation: The tree that the list represents is shown.
The path sum is (3 + 5) + (3 + 1) = 12.


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

1⃣Создание структуры дерева:
Пройдите по массиву nums и для каждого элемента создайте узел дерева.
Сохраните узлы в словаре для удобного доступа по их позиции.

2⃣Связывание узлов:
Пройдите по массиву и свяжите узлы друг с другом, исходя из их позиции и глубины.
Найдите левого и правого ребенка для каждого узла и установите соответствующие связи.

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

😎 Решение:
class Solution {
public:
int pathSum(vector<int>& nums) {
unordered_map<int, int> tree;

for (int num : nums) {
int depth = num / 100;
int pos = (num / 10) % 10;
int val = num % 10;
tree[depth * 10 + pos] = val;
}

return dfs(tree, 1, 1, 0);
}

private:
int dfs(unordered_map<int, int>& tree, int depth, int pos, int currentSum) {
int key = depth * 10 + pos;
if (!tree.count(key)) {
return 0;
}
currentSum += tree[key];
int leftChild = (depth + 1) * 10 + 2 * pos - 1;
int rightChild = (depth + 1) * 10 + 2 * pos;
if (!tree.count(leftChild) && !tree.count(rightChild)) {
return currentSum;
}
return dfs(tree, depth + 1, 2 * pos - 1, currentSum) + dfs(tree, depth + 1, 2 * pos, currentSum);
}
};


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

Вам дан неориентированный взвешенный граф из n узлов (индексация с нуля), представленный списком ребер, где edges[i] = [a, b] является неориентированным ребром, соединяющим узлы a и b с вероятностью успешного прохождения этого ребра succProb[i].

Даны два узла start и end, найдите путь с максимальной вероятностью успеха, чтобы перейти от start к end, и верните его вероятность успеха.

Если пути от start до end не существует, верните 0. Ваш ответ будет принят, если он отличается от правильного ответа не более чем на 1e-5.

Пример:
Input: n = 3, edges = [[0,1],[1,2],[0,2]], succProb = [0.5,0.5,0.2], start = 0, end = 2
Output: 0.25000
Explanation: There are two paths from start to end, one having a probability of success = 0.2 and the other has 0.5 * 0.5 = 0.25.


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

1⃣Инициализируйте массив maxProb как максимальную вероятность достижения каждого узла из начального узла, установите maxProb[start] равным 1.

2⃣Расслабьте все ребра: для каждого ребра (u, v), если найдена более высокая вероятность достижения u через это ребро, обновите max_prob[u] как max_prob[u] = max_prob[v] * path_prob. Если найдена более высокая вероятность достижения v через это ребро, обновите max_prob[v].

3⃣Если нам не удается обновить какой-либо узел с более высокой вероятностью, мы можем остановить итерацию, перейдя к шагу 4. В противном случае повторяйте шаг 2, пока все ребра не будут расслаблены n - 1 раз.
Верните max_prob[end].

😎 Решение:
class Solution {
public:
double maxProbability(int n, vector<vector<int>>& edges, vector<double>& succProb, int start, int end) {
vector<double> maxProb(n, 0.0);
maxProb[start] = 1.0;

for (int i = 0; i < n - 1; i++) {
bool hasUpdate = false;
for (int j = 0; j < edges.size(); j++) {
int u = edges[j][0];
int v = edges[j][1];
double pathProb = succProb[j];
if (maxProb[u] * pathProb > maxProb[v]) {
maxProb[v] = maxProb[u] * pathProb;
hasUpdate = true;
}
if (maxProb[v] * pathProb > maxProb[u]) {
maxProb[u] = maxProb[v] * pathProb;
hasUpdate = true;
}
}
if (!hasUpdate) {
break;
}
}

return maxProb[end];
}
};


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

Вам даны списки регионов, в которых первый регион каждого списка включает все остальные регионы этого списка. Естественно, если регион x содержит другой регион y, то x больше y. Также, по определению, регион x содержит сам себя. Даны два региона: region1 и region2, верните наименьший регион, который содержит их оба. Если вам даны регионы r1, r2 и r3 такие, что r1 включает r3, то гарантированно не существует r2 такого, что r2 включает r3. Гарантированно существует наименьший регион.

Пример:
Input:
regions = [["Earth","North America","South America"],
["North America","United States","Canada"],
["United States","New York","Boston"],
["Canada","Ontario","Quebec"],
["South America","Brazil"]],
region1 = "Quebec",
region2 = "New York"
Output: "North America"


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

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

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

3⃣Найдем последний общий регион в путях двух заданных регионов.

😎 Решение:
class Solution {
public:
string findSmallestRegion(vector<vector<string>>& regions, string region1, string region2) {
unordered_map<string, string> parent;

for (const auto& regionList : regions) {
for (int i = 1; i < regionList.size(); i++) {
parent[regionList[i]] = regionList[0];
}
}

unordered_set<string> ancestors1;
while (region1 != "") {
ancestors1.insert(region1);
region1 = parent[region1];
}

while (ancestors1.find(region2) == ancestors1.end()) {
region2 = parent[region2];
}

return region2;
}
};


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

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

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


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

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

2⃣Храните все сериализованные представления поддеревьев в хэш-таблице и отслеживайте частоту их появления.

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

😎 Решение:
struct TreeNode {
int val;
TreeNode *left, *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};

class Solution {
public:
vector<TreeNode*> findDuplicateSubtrees(TreeNode* root) {
unordered_map<string, int> count;
vector<TreeNode*> result;
serialize(root, count, result);
return result;
}

private:
string serialize(TreeNode* node, unordered_map<string, int>& count, vector<TreeNode*>& result) {
if (!node) return "#";
string serial = to_string(node->val) + "," + serialize(node->left, count, result) + "," + serialize(node->right, count, result);
if (++count[serial] == 2) {
result.push_back(node);
}
return serial;
}
};


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

Дана строка expression, представляющая произвольно вложенные тернарные выражения, вычислите это выражение и верните его результат.

Можно всегда считать, что данное выражение является корректным и содержит только цифры, '?', ':', 'T' и 'F', где 'T' означает истину, а 'F' - ложь. Все числа в выражении являются однозначными числами (т.е. в диапазоне от 0 до 9).

Условные выражения группируются справа налево (как обычно в большинстве языков), и результат выражения всегда будет либо цифрой, либо 'T', либо 'F'.

Пример:
Input: expression = "T?2:3"
Output: "2"
Explanation: If true, then result is 2; otherwise result is 3.


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

1⃣Определите вспомогательную функцию isValidAtomic(s), которая принимает строку s и возвращает True, если s является допустимым атомарным выражением. В противном случае функция возвращает False. Функция будет вызываться только с пятисимвольными строками. Если все следующие условия выполнены, функция возвращает True, иначе - False: s[0] является T или F. s[1] является ?. s[2] является T, F или цифрой от 0 до 9. s[3] является :. s[4] является T, F или цифрой от 0 до 9.

2⃣Определите вспомогательную функцию solveAtomic(s), которая принимает строку s и возвращает значение атомарного выражения. Значение атомарного выражения равно E1, если B - это T, иначе значение равно E2. Функция будет вызываться только с пятисимвольными строками и возвращать один символ:.

3⃣Если s[0] является T, функция возвращает s[2], иначе возвращает s[4]. В функции parseTernary(expression) уменьшайте выражение до тех пор, пока не останется односимвольная строка. Инициализируйте j как expression.size() - 1 (это будет самый правый индекс окна). Пока самое правое окно длиной 5 не является допустимым атомарным выражением, уменьшайте j на 1. Когда будет найдено самое правое допустимое атомарное выражение, решите его и уменьшите до одного символа. Замените самое правое допустимое атомарное выражение одним символом, после чего длина выражения уменьшится на 4. В итоге останется односимвольная строка, которую и верните.

😎 Решение:
class Solution {
public:
string parseTernary(string expression) {
auto isValidAtomic = [](const string& s) {
return (s[0] == 'T' || s[0] == 'F') &&
s[1] == '?' &&
(s[2] == 'T' || s[2] == 'F' || isdigit(s[2])) &&
s[3] == ':' &&
(s[4] == 'T' || s[4] == 'F' || isdigit(s[4]));
};

auto solveAtomic = [](const string& s) {
return s[0] == 'T' ? s[2] : s[4];
};

while (expression.size() != 1) {
int j = expression.size() - 1;
while (!isValidAtomic(expression.substr(j-4, 5))) {
j -= 1;
}
expression = expression.substr(0, j-4) + solveAtomic(expression.substr(j-4, 5)) + expression.substr(j+1);
}

return expression;
}
};


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

Сообщение, содержащее буквы от A-Z, может быть закодировано в цифры с помощью следующего отображения: 'A' -> "1" 'B' -> "2" ... 'Z' -> "26" Чтобы декодировать закодированное сообщение, все цифры должны быть сгруппированы, а затем снова преобразованы в буквы с помощью обратного отображения (может быть несколько способов). Например, "11106" может быть преобразовано в: "AAJF" с группировкой (1 1 10 6) "KJF" с группировкой (11 10 6) Обратите внимание, что группировка (1 11 06) недействительна, поскольку "06" не может быть преобразовано в "F", так как "6" отличается от "06". В дополнение к вышеуказанным преобразованиям кодированное сообщение может содержать символ "*", который может представлять любую цифру от "1" до "9" ("0" исключается). Например, кодированное сообщение "1*" может представлять любое из кодированных сообщений "11", "12", "13", "14", "15", "16", "17", "18" или "19". Декодирование "1*" эквивалентно декодированию любого из кодированных сообщений, которые оно может представлять. Если задана строка s, состоящая из цифр и символов '*', верните количество способов ее декодирования. Поскольку ответ может быть очень большим, верните его по модулю 109 + 7.

Пример:
Input: s = "*"
Output: 9

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

1⃣Инициализация: Создайте массив dp, где dp[i] представляет количество способов декодирования подстроки s[0:i]. Установите начальные значения dp[0] = 1 (пустая строка имеет один способ декодирования).

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

3⃣Модульное вычисление: Поскольку количество способов декодирования может быть большим, вычисляйте результаты по модулю 10^9 + 7.

😎 Решение:
class Solution {
public:
int numDecodings(string s) {
const int MOD = 1000000007;
int n = s.size();
vector<long> dp(n + 1, 0);
dp[0] = 1;

for (int i = 1; i <= n; i++) {
if (s[i - 1] == '*') {
dp[i] = 9 * dp[i - 1];
} else if (s[i - 1] != '0') {
dp[i] = dp[i - 1];
}

if (i > 1) {
if (s[i - 2] == '*') {
if (s[i - 1] == '*') {
dp[i] += 15 * dp[i - 2];
} else if (s[i - 1] <= '6') {
dp[i] += 2 * dp[i - 2];
} else {
dp[i] += dp[i - 2];
}
} else if (s[i - 2] == '1') {
if (s[i - 1] == '*') {
dp[i] += 9 * dp[i - 2];
} else {
dp[i] += dp[i - 2];
}
} else if (s[i - 2] == '2') {
if (s[i - 1] == '*') {
dp[i] += 6 * dp[i - 2];
} else if (s[i - 1] <= '6') {
dp[i] += dp[i - 2];
}
}
}

dp[i] %= MOD;
}

return dp[n];
}
};


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 1019. Next Greater Node In Linked List
Сложность: medium

Вам дана голова связного списка с n узлами. Для каждого узла в списке найдите значение следующего большего узла. То есть для каждого узла найдите значение первого узла, который находится рядом с ним и имеет строго большее значение, чем он. Верните целочисленный массив answer, где answer[i] - это значение следующего большего узла ith-узла (с индексацией по 1). Если у узла ith нет следующего большего узла, установите answer[i] = 0.

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


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

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

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

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

😎 Решение:
class ListNode {
public:
int val;
ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};

class Solution {
public:
vector<int> nextLargerNodes(ListNode* head) {
vector<int> values;
while (head) {
values.push_back(head->val);
head = head->next;
}

vector<int> answer(values.size(), 0);
stack<int> s;

for (int i = 0; i < values.size(); ++i) {
while (!s.empty() && values[s.top()] < values[i]) {
answer[s.top()] = values[i];
s.pop();
}
s.push(i);
}

return answer;
}
};


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