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

Тесты t.iss.one/+zYofcX2VLTM3MGMy
Вопросы собесов t.iss.one/+BTbqlW1VbIFmYmVi
Вакансии t.iss.one/+za2mJYs4riAzMzFi
Download Telegram
Задача: 1247. Minimum Swaps to Make Strings Equal
Сложность: hard

Вам даны две строки s1 и s2 одинаковой длины, состоящие только из букв "x" и "y". Ваша задача - сделать эти две строки равными друг другу. Вы можете поменять местами любые два символа, принадлежащие разным строкам, что означает: поменять местами s1[i] и s2[j]. Верните минимальное количество обменов, необходимое для того, чтобы сделать s1 и s2 равными, или верните -1, если это невозможно сделать.

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


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

1⃣Подсчет несоответствующих пар:
Пройдите по строкам s1 и s2, чтобы подсчитать количество пар xy и yx. Пара xy возникает, когда s1[i] равно 'x', а s2[i] равно 'y'. Пара yx возникает, когда s1[i] равно 'y', а s2[i] равно 'x'.

2⃣Проверка четности:
Если сумма количества пар xy и yx нечетная, то невозможно сделать строки равными, поскольку каждая замена уменьшает сумму несоответствующих пар на 2. В этом случае верните -1.

3⃣Вычисление минимального количества замен:
Если количество пар xy четное и количество пар yx четное, то каждые две пары xy и каждые две пары yx можно обменять за один ход. Поэтому минимальное количество замен равно xy // 2 + yx // 2.
Если количество пар xy нечетное и количество пар yx нечетное, то мы можем обменять одну пару xy и одну пару yx за два хода. Поэтому минимальное количество замен равно xy // 2 + yx // 2 + 2.

😎 Решение:
class Solution {
public:
int minimumSwap(string s1, string s2) {
int xy = 0, yx = 0;
for (int i = 0; i < s1.length(); ++i) {
if (s1[i] == 'x' && s2[i] == 'y') {
++xy;
} else if (s1[i] == 'y' && s2[i] == 'x') {
++yx;
}
}
if ((xy + yx) % 2 != 0) {
return -1;
}
return xy / 2 + yx / 2 + (xy % 2) * 2;
}
};


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

В этой задаче дерево — это неориентированный граф, который является связным и не содержит циклов.

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

Верните ребро, которое можно удалить, чтобы результирующий граф стал деревом из n узлов. Если существует несколько ответов, верните тот, который встречается последним в исходных данных.
Пример:
Input: edges = [[1,2],[1,3],[2,3]]
Output: [2,3]


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

1⃣Для каждого ребра (u, v) создайте представление графа с использованием списка смежности. Это позволит легко выполнять обход в глубину (DFS) для проверки соединений между узлами.

2⃣Выполняйте обход в глубину для каждого ребра, временно удаляя его из графа. Проверьте, можно ли соединить узлы u и v с помощью обхода в глубину. Если узлы остаются соединенными, значит, это ребро является дублирующимся.

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

😎 Решение:
#include <vector>
#include <unordered_set>
#include <array>

class Solution {
public:
std::unordered_set<int> seen;
static const int MAX_EDGE_VAL = 1000;

std::vector<int> findRedundantConnection(std::vector<std::vector<int>>& edges) {
std::array<std::vector<int>, MAX_EDGE_VAL + 1> graph;
for (auto& edge : edges) {
seen.clear();
if (!graph[edge[0]].empty() && !graph[edge[1]].empty() && dfs(graph, edge[0], edge[1])) {
return edge;
}
graph[edge[0]].push_back(edge[1]);
graph[edge[1]].push_back(edge[0]);
}
throw std::runtime_error("No redundant connection found");
}

bool dfs(const std::array<std::vector<int>, MAX_EDGE_VAL + 1>& graph, int source, int target) {
if (!seen.count(source)) {
seen.insert(source);
if (source == target) return true;
for (int nei : graph[source]) {
if (dfs(graph, nei, target)) return true;
}
}
return false;
}
};


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

Вам дан корень бинарного дерева.

Зигзагообразный путь для бинарного дерева определяется следующим образом:

Выберите любой узел в бинарном дереве и направление (вправо или влево).
Если текущее направление вправо, перейдите к правому дочернему узлу текущего узла; иначе перейдите к левому дочернему узлу.
Измените направление с вправо на влево или с влево на вправо.
Повторяйте второй и третий шаги, пока не сможете двигаться по дереву.
Длина зигзагообразного пути определяется как количество посещенных узлов минус 1 (один узел имеет длину 0).

Верните длину самого длинного зигзагообразного пути, содержащегося в этом дереве.

Пример:
Input: s = "rat"
Output: "art"
Explanation: The word "rat" becomes "art" after re-ordering it with the mentioned algorithm.


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

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

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

3⃣Рекурсивный вызов для левого и правого дочерних узлов:
Рекурсивно вызывайте функцию dfs для левого и правого дочерних узлов с обновленными параметрами длины и направления.

😎 Решение:
#include <algorithm>

struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};

class Solution {
public:
int maxLength = 0;

int longestZigZag(TreeNode* root) {
dfs(root, true, 0);
dfs(root, false, 0);
return maxLength;
}

void dfs(TreeNode* node, bool isLeft, int length) {
if (!node) return;
maxLength = std::max(maxLength, length);
if (isLeft) {
dfs(node->left, false, length + 1);
dfs(node->right, true, 1);
} else {
dfs(node->right, true, length + 1);
dfs(node->left, false, 1);
}
}
};


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

Задано целое положительное число k, необходимо найти длину наименьшего целого положительного числа n, такого, что n делится на k, и n содержит только цифру 1. Верните длину n. Если такого n не существует, верните -1. Примечание: n может не поместиться в 64-битное знаковое целое число.

Пример:
Input: k = 1
Output: 1


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

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

2⃣Итеративное нахождение числа:
Используйте цикл, чтобы умножать num на 10 и добавлять 1 в каждой итерации, и каждый раз вычисляйте остаток от деления num на k.
Увеличивайте length на 1 в каждой итерации.
Если в какой-то итерации num % k == 0, верните length.

3⃣Проверка бесконечного цикла:
Если цикл длится слишком долго (например, 10^6 итераций), верните -1, чтобы предотвратить бесконечный цикл для случаев, когда решение не существует.

😎 Решение:
class Solution {
public:
int smallestRepunitDivByK(int k) {
int num = 1, length = 1;
unordered_set<int> seen;

while (num % k != 0) {
if (seen.count(num % k)) {
return -1;
}
seen.insert(num % k);
num = (num * 10 + 1) % k;
length++;
}

return length;
}
};


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

Дан корень бинарного дерева. Верните максимальное среднее значение поддерева этого дерева. Ответы, отличающиеся от фактического ответа не более чем на 10^-5, будут приняты.

Поддерево дерева — это любой узел этого дерева плюс все его потомки.

Среднее значение дерева — это сумма его значений, деленная на количество узлов.

Пример:
Input: root = [5,6,1]
Output: 6.00000
Explanation:
For the node with value = 5 we have an average of (5 + 6 + 1) / 3 = 4.
For the node with value = 6 we have an average of 6 / 1 = 6.
For the node with value = 1 we have an average of 1 / 1 = 1.
So the answer is 6 which is the maximum.


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

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

2⃣Постобход (postorder traversal):
Реализуйте функцию maxAverage, которая выполняет постобход дерева, вычисляя nodeCount, valueSum и maxAverage для каждого узла, начиная с дочерних узлов и продвигаясь к родительским узлам.

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

😎 Решение:
class Solution {
public:
struct State {
int nodeCount;
int valueSum;
double maxAverage;

State(int nodes, int sum, double maxAvg)
: nodeCount(nodes), valueSum(sum), maxAverage(maxAvg) {}
};

double maximumAverageSubtree(TreeNode* root) {
return maxAverage(root).maxAverage;
}

private:
State maxAverage(TreeNode* root) {
if (root == nullptr) {
return State(0, 0, 0);
}

State left = maxAverage(root->left);
State right = maxAverage(root->right);

int nodeCount = left.nodeCount + right.nodeCount + 1;
int sum = left.valueSum + right.valueSum + root->val;
double currentAverage = static_cast<double>(sum) / nodeCount;
double maxAverage = std::max(currentAverage, std::max(left.maxAverage, right.maxAverage));

return State(nodeCount, sum, maxAverage);
}
};


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

Допустимым кодированием массива слов является любая опорная строка s и массив индексов indices, такие что:

words.length == indices.length
Опорная строка s заканчивается символом '#'.
Для каждого индекса indices[i], подстрока строки s, начинающаяся с indices[i] и заканчивающаяся (но не включительно) следующим символом '#', равна words[i].
Дан массив слов, верните длину самой короткой возможной опорной строки s для любого допустимого кодирования слов.

Пример:
Input: words = ["time", "me", "bell"]
Output: 10
Explanation: A valid encoding would be s = "time#bell#" and indices = [0, 2, 5].
words[0] = "time", the substring of s starting from indices[0] = 0 to the next '#' is underlined in "time#bell#"
words[1] = "me", the substring of s starting from indices[1] = 2 to the next '#' is underlined in "time#bell#"
words[2] = "bell", the substring of s starting from indices[2] = 5 to the next '#' is underlined in "time#bell#"


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

1⃣Поскольку слово имеет не более 6 собственных суффиксов (так как words[i].length <= 7), давайте итерироваться по всем из них. Для каждого собственного суффикса мы попытаемся удалить его из нашего списка слов. Для эффективности сделаем words множеством.

2⃣Затем создадим список оставшихся слов и сформируем опорную строку, объединяя каждое слово с символом '#'.

3⃣В конце вернем длину полученной опорной строки.

😎 Решение:
class Solution {
public:
int minimumLengthEncoding(vector<string>& words) {
unordered_set<string> good(words.begin(), words.end());
for (const string& word : words) {
for (int k = 1; k < word.length(); ++k) {
good.erase(word.substr(k));
}
}
int length = 0;
for (const string& word : good) {
length += word.length() + 1;
}
return length;
}
};


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 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:
vector<int> threeEqualParts(vector<int>& arr) {
int ones = 0;
for (int num : arr) {
ones += num;
}
if (ones % 3 != 0) return {-1, -1};
if (ones == 0) return {0, (int)arr.size() - 1};

int partOnes = ones / 3;
int first = 0, second = 0, third = 0, cnt = 0;
for (int i = 0; i < arr.size(); 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.size() && arr[first] == arr[second] && arr[first] == arr[third]) {
first++;
second++;
third++;
}

if (third == arr.size()) return {first - 1, second};
return {-1, -1};
}
};


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

Даны два неотрицательных целых числа num1 и num2, представленные в виде строк. Верните их произведение, также в виде строки.
Нельзя использовать встроенные BigInteger или преобразование строк в числа напрямую.

Пример:
Input: num1 = "2", num2 = "3" Output: "6"

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

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

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

3⃣Просуммировать все промежуточные результаты, удалить ведущие нули, перевернуть результат и вернуть как строку

😎 Решение:
class Solution {
private:
string sumResults(vector<vector<int>>& results) {
vector<int> answer = results.back();
results.pop_back();
vector<int> newAnswer;
int carry = 0;

for (vector<int>& result : results) {
newAnswer.clear();
int maxLen = max(answer.size(), result.size());
for (int i = 0; i < maxLen || carry; ++i) {
int sum = carry + (i < result.size() ? result[i] : 0) + (i < answer.size() ? answer[i] : 0);
newAnswer.push_back(sum % 10);
carry = sum / 10;
}
answer = move(newAnswer);
}
string finalAnswer;
for (int digit : answer) finalAnswer.push_back(digit + '0');
return finalAnswer;
}

vector<int> multiplyOneDigit(string& firstNumber, char secondNumberDigit, int numZeros) {
vector<int> currentResult(numZeros, 0);
int carry = 0;

for (char firstNumberDigit : firstNumber) {
int multiplication = (secondNumberDigit - '0') * (firstNumberDigit - '0') + carry;
currentResult.push_back(multiplication % 10);
carry = multiplication / 10;
}

if (carry) currentResult.push_back(carry);
return currentResult;
}

public:
string multiply(string firstNumber, string secondNumber) {
if (firstNumber == "0" || secondNumber == "0") return "0";

reverse(firstNumber.begin(), firstNumber.end());
reverse(secondNumber.begin(), secondNumber.end());

vector<vector<int>> results;
for (int i = 0; i < secondNumber.size(); ++i) {
results.push_back(multiplyOneDigit(firstNumber, secondNumber[i], i));
}
string answer = sumResults(results);
reverse(answer.begin(), answer.end());
return answer;
}
};


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

Дана строка s. Вернуть длину самой длинной повторяющейся подстроки. Если повторяющаяся подстрока отсутствует, вернуть 0.

Пример:
Input: s = "abcd"
Output: 0
Explanation: There is no repeating substring.


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

1⃣Перемещайте скользящее окно длиной L по строке длиной N.

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

3⃣Очевидный недостаток этого подхода — большое потребление памяти в случае длинных строк.

😎 Решение:
class Solution {
public:
int search(int L, int n, string S) {
unordered_set<string> seen;
for (int start = 0; start <= n - L; ++start) {
string tmp = S.substr(start, L);
if (seen.count(tmp)) return start;
seen.insert(tmp);
}
return -1;
}

int longestRepeatingSubstring(string S) {
int n = S.length();
int left = 1, right = n;
while (left <= right) {
int L = left + (right - left) / 2;
if (search(L, n, S) != -1) {
left = L + 1;
} else {
right = L - 1;
}
}
return left - 1;
}
};


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

Дана строка s и символ c, который встречается в s. Верните массив целых чисел answer, где answer.length == s.length, и answer[i] - это расстояние от индекса i до ближайшего появления символа c в строке s.

Расстояние между двумя индексами i и j равно abs(i - j), где abs - это функция абсолютного значения.

Пример:
Input: s = "loveleetcode", c = "e"
Output: [3,2,1,0,1,0,0,1,2,2,1,0]
Explanation: The character 'e' appears at indices 3, 5, 6, and 11 (0-indexed).
The closest occurrence of 'e' for index 0 is at index 3, so the distance is abs(0 - 3) = 3.
The closest occurrence of 'e' for index 1 is at index 3, so the distance is abs(1 - 3) = 2.
For index 4, there is a tie between the 'e' at index 3 and the 'e' at index 5, but the distance is still the same: abs(4 - 3) == abs(4 - 5) = 1.
The closest occurrence of 'e' for index 8 is at index 6, so the distance is abs(8 - 6) = 2.


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

1⃣При проходе слева направо будем запоминать индекс prev последнего символа C, который мы видели. Тогда ответ будет i - prev.

2⃣При проходе справа налево будем запоминать индекс prev последнего символа C, который мы видели. Тогда ответ будет prev - i.

3⃣Мы берем минимальное значение из этих двух ответов для создания нашего окончательного ответа.

😎 Решение:
class Solution {
public:
vector<int> shortestToChar(string S, char C) {
int N = S.size();
vector<int> ans(N, N);
int prev = -N;

for (int i = 0; i < N; ++i) {
if (S[i] == C) {
prev = i;
}
ans[i] = i - prev;
}

prev = 2 * N;
for (int i = N - 1; i >= 0; --i) {
if (S[i] == C) {
prev = i;
}
ans[i] = min(ans[i], prev - i);
}

return ans;
}
};


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Задача: 943. Find the Shortest Superstring
Сложность: hard

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

Пример:
Input: words = ["alex","loves","leetcode"]
Output: "alexlovesleetcode"


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

1⃣Реализовать функцию overlap для вычисления максимального перекрытия двух строк, где одна строка заканчивается, а другая начинается.

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

3⃣Вернуть результат.

😎 Решение:
class Solution {
public:
string shortestSuperstring(vector<string>& words) {
while (words.size() > 1) {
int maxOverlap = -1;
int l = 0, r = 0;
string merged;
for (int i = 0; i < words.size(); i++) {
for (int j = 0; j < words.size(); j++) {
if (i != j) {
int ovlp = overlap(words[i], words[j]);
if (ovlp > maxOverlap) {
maxOverlap = ovlp;
l = i;
r = j;
merged = merge(words[i], words[j], ovlp);
}
}
}
}
words[l] = merged;
words.erase(words.begin() + r);
}
return words[0];
}

private:
int overlap(const string& a, const string& b) {
int maxOverlap = 0;
for (int i = 1; i <= min(a.size(), b.size()); i++) {
if (a.substr(a.size() - i) == b.substr(0, i)) {
maxOverlap = i;
}
}
return maxOverlap;
}

string merge(const string& a, const string& b, int overlapLen) {
return a + b.substr(overlapLen);
}
};


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Forwarded from easyoffer
Я боялся, что провалю собеседование. Так появился easyoffer

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

Типа… ты потратил месяцы на то, чтобы учиться, писал pet-проекты, собирал резюме, рассылаешь отклики — и всё может закончиться на одном-единственном вопросе, на который ты не знаешь ответ.

Я реально боялся.
Я смотрел видео mock-собеседований на YouTube, останавливал каждое, выписывал вопросы в Notion. Потом вручную писал к ним ответы. И потом ещё по нескольку раз перечитывал. Такой вот "тренажёр" на коленке.

📎 (там на картинке — один из моих реальных списков в Notion, ставь 🔥 если тоже так делал)

В какой-то момент я посчитал — у меня уже было выписано больше 500 вопросов. Я почувствовал ужас.
Потому что невозможно всё это зазубрить. А что, если спросят как раз тот, к которому я не успел подготовиться?..

Тогда и пришла идея

А что если понять, какие из вопросов встречаются чаще всего? Чтобы не учить всё подряд, а сфокусироваться на главном.

Так родился easyoffer.

Сначала — просто как пет-проект, чтобы показать в резюме и подготовиться к собесам. А потом оказалось, что он реально помогает людям. За первые месяцы его посетили сотни тысяч человек. И я понял: это больше, чем просто пет-проект.

Сейчас я делаю EasyOffer 2.0
И уже не один, а вместе с вами.

В новой версии будут:
– вопросы из реальных собесов, с фильтрацией по грейду, компании, типу интервью
– тренажёр с карточками (по принципу интервальных повторений — как в Anki)
– база задач с интервью
– тренажёр «реальное собеседование», чтобы отрепетировать как в жизни

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

Я делаю всё на свои деньги. Никаких инвесторов. Только вы и я.

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

Все, кто поддержат проект до релиза, получат:

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

Поддержать 👉 https://planeta.ru/campaigns/easyoffer

Спасибо, что верите в этот проект 🙌
Задача: 925. Long Pressed Name
Сложность: easy

Ваш друг набирает на клавиатуре свое имя. Иногда, при наборе символа c, клавиша может быть долго нажата, и символ будет набран 1 или более раз. Вы исследуете набранные символы клавиатуры. Верните True, если возможно, что это было имя вашего друга, при этом некоторые символы (возможно, ни один) не были долго нажаты.

Пример:
Input: name = "alex", typed = "aaleex"
Output: true


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

1⃣Инициализировать два указателя i и j для строки имени и набранной строки соответственно.

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

3⃣Вернуть True, если все символы имени были обработаны, иначе False.

😎 Решение:
class Solution {
public:
bool isLongPressedName(string name, string typed) {
int i = 0, j = 0;
while (j < typed.size()) {
if (i < name.size() && name[i] == typed[j]) {
i++;
} else if (j == 0 || typed[j] != typed[j - 1]) {
return false;
}
j++;
}
return i == name.size();
}
};


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

Вам дан массив из n строк strs, все одинаковой длины.
Мы можем выбрать любые индексы для удаления, и мы удаляем все символы в этих индексах для каждой строки.

Например, если у нас есть strs = ["abcdef","uvwxyz"] и индексы удаления {0, 2, 3}, то итоговый массив после удаления будет ["bef", "vyz"].
Предположим, мы выбрали набор индексов удаления answer таким образом, что после удаления итоговый массив имеет каждую строку (ряд) в лексикографическом порядке. (т.е. (strs[0][0] <= strs[0][1] <= ... <= strs[0][strs[0].length - 1]) и (strs[1][0] <= strs[1][1] <= ... <= strs[1][strs[1].length - 1]) и так далее). Верните минимально возможное значение answer.length.

Пример:
Input: strs = ["babca","bbazb"]
Output: 3
Explanation: After deleting columns 0, 1, and 4, the final array is strs = ["bc", "az"].
Both these rows are individually in lexicographic order (ie. strs[0][0] <= strs[0][1] and strs[1][0] <= strs[1][1]).
Note that strs[0] > strs[1] - the array strs is not necessarily in lexicographic order.


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

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

2⃣Используйте динамическое программирование, чтобы решить проблему. Пусть dp[k] будет количеством столбцов, которые сохраняются, начиная с позиции k. Мы будем искать максимальное значение dp[k], чтобы найти количество столбцов, которые нужно сохранить.

3⃣Итоговое количество удаляемых столбцов будет равно общей длине строк минус количество сохраненных столбцов.

😎 Решение:
class Solution {
public:
int minDeletionSize(vector<string>& A) {
int W = A[0].size();
vector<int> dp(W, 1);

for (int i = W - 2; i >= 0; --i) {
for (int j = i + 1; j < W; ++j) {
bool valid = true;
for (const string& row : A) {
if (row[i] > row[j]) {
valid = false;
break;
}
}
if (valid) {
dp[i] = max(dp[i], 1 + dp[j]);
}
}
}

return W - *max_element(dp.begin(), dp.end());
}
};


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

Массив является монотонным, если он либо монотонно возрастает, либо монотонно убывает. Массив nums является монотонно возрастающим, если для всех i <= j, nums[i] <= nums[j]. Массив nums является монотонно убывающим, если для всех i <= j, nums[i] >= nums[j]. Если задан целочисленный массив nums, верните true, если данный массив монотонный, или false в противном случае.

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


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

1⃣Определить два флага: increasing и decreasing.

2⃣Пройтись по массиву:
Если текущий элемент больше следующего, установить increasing в false.
Если текущий элемент меньше следующего, установить decreasing в false.

3⃣Вернуть true, если хотя бы один из флагов true, иначе вернуть false.

😎 Решение:
class Solution {
public:
bool isMonotonic(vector<int>& nums) {
bool increasing = true, decreasing = true;
for (int i = 1; i < nums.size(); ++i) {
if (nums[i] > nums[i - 1]) decreasing = false;
if (nums[i] < nums[i - 1]) increasing = false;
}
return increasing || decreasing;
}
};


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

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

Пример:
Input: arr = [1,1,2,2,3,3,4,4,5,5], target = 8
Output: 20


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

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

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

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

😎 Решение:
class Solution {
public:
int minMalwareSpread(vector<vector<int>>& graph, vector<int>& initial) {
int n = graph.size();
unordered_set<int> initialSet(initial.begin(), initial.end());
sort(initial.begin(), initial.end());
int minInfected = INT_MAX;
int bestNode = initial[0];

for (int node : initial) {
unordered_set<int> infected(initialSet);
infected.erase(node);
for (int i : initialSet) {
if (i != node) {
dfs(graph, i, infected);
}
}
if (infected.size() < minInfected) {
minInfected = infected.size();
bestNode = node;
}
}

return bestNode;
}

private:
void dfs(vector<vector<int>>& graph, int node, unordered_set<int>& infected) {
for (int neighbor = 0; neighbor < graph.size(); neighbor++) {
if (graph[node][neighbor] == 1 && infected.find(neighbor) == infected.end()) {
infected.insert(neighbor);
dfs(graph, neighbor, infected);
}
}
}
};


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

Дан массив intervals, где каждый элемент — это временной интервал встречи [start, end].
Нужно определить, может ли человек посетить все встречи, то есть проверить, нет ли перекрывающихся интервалов.

Пример:
Input: intervals = [[0,30],[5,10],[15,20]] Output: false

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

1⃣Напишите функцию overlap, которая проверяет, пересекаются ли два интервала.
Если начало одного интервала попадает внутрь другого — возвращаем true.

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

3⃣Если все интервалы проверены и пересечений нет — возвращаем true.

😎 Решение:
class Solution {
public:
bool overlap(vector<int>& interval1, vector<int>& interval2) {
return interval1[0] >= interval2[0] && interval1[0] < interval2[1]
|| interval2[0] >= interval1[0] && interval2[0] < interval1[1];
}

bool canAttendMeetings(vector<vector<int>>& intervals) {
for (size_t i = 0; i < intervals.size(); i++) {
for (size_t j = i + 1; j < intervals.size(); j++) {
if (overlap(intervals[i], intervals[j])) {
return false;
}
}
}
return true;
}
};


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Задача: 1178. Number of Valid Words for Each PuzzleHardTopicsHint
Сложность: 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⃣Постройте карту. Для каждого слова в списке words:
Преобразуйте его в битовую маску его символов. Если эта битовая маска не была ранее встречена, сохраните ее в качестве ключа в карте со значением 1. Если она была ранее встречена, увеличьте счетчик для этой битовой маски на 1.

2⃣Подсчитайте количество допустимых слов для каждой головоломки. Для каждой головоломки в списке puzzles:
Преобразуйте ее в битовую маску ее символов. Итерируйте по каждой возможной подмаске, содержащей первую букву головоломки (puzzle[i][0]). Слово является допустимым для головоломки, если его битовая маска совпадает с одной из подмасок головоломки.

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

😎 Решение:
class Solution {
private:
int bitmask(string word) {
int mask = 0;
for (char letter : word) {
mask |= 1 << (letter - 'a');
}
return mask;
}

public:
vector<int> findNumOfValidWords(vector<string>& words, vector<string>& puzzles) {
unordered_map<int, int> wordCount;
for (string word : words) {
int mask = bitmask(word);
wordCount[mask]++;
}

vector<int> result;
for (string puzzle : puzzles) {
int first = 1 << (puzzle[0] - 'a');
int count = wordCount[first];
int mask = bitmask(puzzle.substr(1));
for (int submask = mask; submask > 0; submask = (submask - 1) & mask) {
count += wordCount[submask | first];
}
result.push_back(count);
}
return result;
}
};


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

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

Пример:
Input: strs = ["cba","daf","ghi"]
Output: 1


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

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

2⃣Пройти по каждому столбцу от 0 до длины строки.
Для каждого столбца проверить, отсортированы ли символы лексикографически.
Если столбец не отсортирован, увеличить count.

3⃣Вернуть count.

😎 Решение:
class Solution {
public:
int minDeletionSize(vector<string>& strs) {
int count = 0;
int rows = strs.size();
int cols = strs[0].size();
for (int col = 0; col < cols; col++) {
for (int row = 1; row < rows; row++) {
if (strs[row][col] < strs[row - 1][col]) {
count++;
break;
}
}
}
return count;
}
};


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