Задача: 30. Substring with Concatenation of All Words #hard
Условие:
Решение:
Пояснение:
Этот код находит все подстроки в строке `str`, которые содержат все слова из вектора `words` в произвольном порядке.
1. Сначала определяется длина слова в векторе `words`.
2. Создается unordered_map `contain`, чтобы хранить количество вхождений каждого слова из `words`.
3. Далее происходит итерация по всем возможным смещениям начала подстроки `j`.
4. Внутри цикла идет итерация по строке `str` с шагом, равным длине слова.
5. Для каждой подстроки текущее слово проверяется на его наличие в `contain`.
6. Если слово содержится в `contain`, увеличивается его количество в текущей найденной подстроке `found`.
7. Затем проверяется, не превосходит ли количество данного слова в `found` их количества в `contain`. Если превосходит, сдвигаем начало найденной подстроки `st` до тех пор, пока это условие не выполняется.
8. Если количество слов в найденной подстроке равно количеству слов в `words`, добавляем индекс начала найденной подстроки в результат `res`.
9. Если текущее слово не содержится в `contain`, сбрасываем `found` и сдвигаем начало найденной подстроки `st` на следующее слово.
10. По окончании всех итераций возвращаем вектор с индексами начала всех найденных подстрок.
Этот код использует сложность O(n), где n - это длина строки `str`, так как он проходит по строке только один раз.
Условие:
Вам дана строка s и массив строк-слов. Все строки слов имеют одинаковую длину.
Объединенная строка — это строка, которая в точности содержит все строки любой перестановки объединенных слов.
Например, если слова = ["ab", "cd", "ef"], то "abcdef", "abefcd", "cdabef", "cdefab", "efabcd" и "efcdab" являются объединенными строками. «acdbef» не является объединенной строкой, поскольку не является объединением какой-либо перестановки слов.
Возвращает массив начальных индексов всех объединенных подстрок в s. Вы можете вернуть ответ в любом порядке.
Решение:
vector<int> findSubstring(string str, vector<string>& words) {
int len = words[0].length();
unordered_map<string, int> contain;
for(string s: words) contain[s]++;
vector<int> res;
for(int j = 0; j < len; j++) {
unordered_map<string, int> found;
int st = j;
for(int i = 0 + j; i < str.size() - len + 1; i += len) {
string curr = str.substr(i, len);
if(contain.find(curr) != contain.end()) {
found[curr]++;
while(found[curr] > contain[curr]) {
found[str.substr(st, len)]--;
st += len;
}
int size = (i - st + len) / len;
if(size == words.size()) {
cout << j << " " << st << " " << i << endl;
res.push_back(st);
}
} else {
found.clear();
st = i + len;
}
}
}
return res;
}Пояснение:
Этот код находит все подстроки в строке `str`, которые содержат все слова из вектора `words` в произвольном порядке.
1. Сначала определяется длина слова в векторе `words`.
2. Создается unordered_map `contain`, чтобы хранить количество вхождений каждого слова из `words`.
3. Далее происходит итерация по всем возможным смещениям начала подстроки `j`.
4. Внутри цикла идет итерация по строке `str` с шагом, равным длине слова.
5. Для каждой подстроки текущее слово проверяется на его наличие в `contain`.
6. Если слово содержится в `contain`, увеличивается его количество в текущей найденной подстроке `found`.
7. Затем проверяется, не превосходит ли количество данного слова в `found` их количества в `contain`. Если превосходит, сдвигаем начало найденной подстроки `st` до тех пор, пока это условие не выполняется.
8. Если количество слов в найденной подстроке равно количеству слов в `words`, добавляем индекс начала найденной подстроки в результат `res`.
9. Если текущее слово не содержится в `contain`, сбрасываем `found` и сдвигаем начало найденной подстроки `st` на следующее слово.
10. По окончании всех итераций возвращаем вектор с индексами начала всех найденных подстрок.
Этот код использует сложность O(n), где n - это длина строки `str`, так как он проходит по строке только один раз.
👍2
Задача: 31. Next Permutation #medium
Условие:
Решение:
Пояснение:
Этот код реализует функцию `nextPermutation`, которая изменяет вектор `nums` на следующую перестановку элементов в лексикографическом порядке.
1. Определяется переменная `n`, содержащая индекс последнего элемента вектора `nums`.
2. Начинается поиск элемента, который можно обменять.
3. Индекс `index` уменьшается до тех пор, пока элементы в векторе упорядочены по убыванию.
4. Если нашелся элемент, который можно обменять, находим следующий наименьший элемент, но больший обменяемого элемента.
5. Обменяем найденные элементы.
6. Сортируем элементы вектора, начиная с позиции `index+1`, чтобы получить следующую перестановку в лексикографическом порядке.
Этот алгоритм работает за O(nlogn) времени из-за сортировки элементов вектора, и может изменить вектор `nums` на следующую перестановку в лексикографическом порядке.
Условие:
Перестановка массива целых чисел — это расположение его членов в последовательность или линейный порядок.
Например, для arr = [1,2,3] следующие перестановки arr: [1,2,3], [1,3,2], [2, 1, 3], [2, 3, 1], [3,1,2], [3,2,1].
Следующая перестановка массива целых чисел — это следующая лексикографически большая перестановка его целого числа. Более формально, если все перестановки массива отсортированы в одном контейнере в соответствии с их лексикографическим порядком, то следующей перестановкой этого массива будет перестановка, следующая за ней в отсортированном контейнере. Если такое расположение невозможно, массив необходимо переупорядочить в наименьшем возможном порядке (т. е. отсортировать по возрастанию).
Например, следующая перестановка arr = [1,2,3] — это [1,3,2].
Аналогично, следующая перестановка arr = [2,3,1] — это [3,1,2].
А следующая перестановка arr = [3,2,1] — это [1,2,3], потому что [3,2,1] не имеет более крупной лексикографической перестановки.
Учитывая массив целых чисел nums, найдите следующую перестановку чисел.
Замена должна быть на месте и использовать только постоянную дополнительную память.
Решение:
class Solution {
public:
void nextPermutation(vector<int>& nums) {
int n = nums.size()-1;
int index = n-1;
while(index >= 0 && nums[index] >= nums[index+1])
index--;
if(index >= 0) {
int nextGreaterElement = nums[index+1];
int nextGreaterIndex = index+1;
for(int i=index+1; i <= n; i++)
{
if(nums[i] > nums[index] && nums[i] < nextGreaterElement)
{
nextGreaterElement = nums[i];
nextGreaterIndex = i;
}
}
swap(nums[index], nums[nextGreaterIndex]);
}
sort(nums.begin()+index+1, nums.end());
}
};Пояснение:
Этот код реализует функцию `nextPermutation`, которая изменяет вектор `nums` на следующую перестановку элементов в лексикографическом порядке.
1. Определяется переменная `n`, содержащая индекс последнего элемента вектора `nums`.
2. Начинается поиск элемента, который можно обменять.
3. Индекс `index` уменьшается до тех пор, пока элементы в векторе упорядочены по убыванию.
4. Если нашелся элемент, который можно обменять, находим следующий наименьший элемент, но больший обменяемого элемента.
5. Обменяем найденные элементы.
6. Сортируем элементы вектора, начиная с позиции `index+1`, чтобы получить следующую перестановку в лексикографическом порядке.
Этот алгоритм работает за O(nlogn) времени из-за сортировки элементов вектора, и может изменить вектор `nums` на следующую перестановку в лексикографическом порядке.
👍1
Задача: 32. Longest Valid Parentheses #hard
Условие:
Решение:
Пояснение:
Этот код реализует функцию `longestValidParentheses`, которая находит длину самой длинной правильной скобочной последовательности в строке `s`. В данной задаче правильная скобочная последовательность - это такая последовательность символов, в которой открывающиеся скобки правильно закрываются.
1. Создается стек `stack`, в котором будут храниться индексы открывающихся скобок.
2. Инициализируется переменная `m` для хранения максимальной длины правильной скобочной последовательности.
3. В стек помещается начальное значение -1, чтобы при открывающейся скобке можно было вычислить длину последовательности.
4. Происходит итерация по всем символам строки `s`.
5. Если символ - открывающая скобка, его индекс помещается в стек.
6. Если символ - закрывающая скобка, из стека удаляется индекс последней открывающей скобки. Если стек пуст, то текущий индекс добавляется в стек, так как некорректная последовательность.
7. Иначе вычисляется длина текущей правильной последовательности и обновляется значение `m`, если это значение больше текущего максимума.
8. В конце возвращается максимальная длина правильной скобочной последовательности.
Этот подход использует стек для отслеживания индексов открывающихся скобок и может быть выполнен за O(n), где n - длина строки `s`.
Условие:
Учитывая строку, содержащую только символы «(» и «)», верните длину самых длинных допустимых (правильно сформированных) круглых скобок.
подстрока
Решение:
class Solution {
public:
int longestValidParentheses(string s) {
stack<int> stack;
int m = 0;
stack.push(-1);
for (int i = 0; i < s.length(); i++) {
if (s[i] == '(') {
stack.push(i);
} else {
stack.pop();
if (stack.empty()) {
stack.push(i);
} else {
m = max(m, (i - stack.top()));
}
}
}
return m;
}
};Пояснение:
Этот код реализует функцию `longestValidParentheses`, которая находит длину самой длинной правильной скобочной последовательности в строке `s`. В данной задаче правильная скобочная последовательность - это такая последовательность символов, в которой открывающиеся скобки правильно закрываются.
1. Создается стек `stack`, в котором будут храниться индексы открывающихся скобок.
2. Инициализируется переменная `m` для хранения максимальной длины правильной скобочной последовательности.
3. В стек помещается начальное значение -1, чтобы при открывающейся скобке можно было вычислить длину последовательности.
4. Происходит итерация по всем символам строки `s`.
5. Если символ - открывающая скобка, его индекс помещается в стек.
6. Если символ - закрывающая скобка, из стека удаляется индекс последней открывающей скобки. Если стек пуст, то текущий индекс добавляется в стек, так как некорректная последовательность.
7. Иначе вычисляется длина текущей правильной последовательности и обновляется значение `m`, если это значение больше текущего максимума.
8. В конце возвращается максимальная длина правильной скобочной последовательности.
Этот подход использует стек для отслеживания индексов открывающихся скобок и может быть выполнен за O(n), где n - длина строки `s`.
Задача: 33. Search in Rotated Sorted Array #medium
Условие:
Решение:
Пояснение:
Данный код реализует функцию `search`, которая выполняет поиск элемента `target` в отсортированном массиве `nums`. В данном случае массив может быть как отсортированным по возрастанию, так и массивом, который был "повернут" (то есть началом может быть какой-то элемент из середины массива).
1. Начальная инициализация левой и правой границ массива.
2. В цикле `while` происходит поиск элемента `target` путем деления текущего отрезка пополам.
3. Если левый, правый или средний элемент равен `target`, то метод возвращает соответствующий индекс.
4. Если массив "повернут", мы уменьшаем правую и увеличиваем левую границу, чтобы сблизиться к участку массива, где нет поворота.
5. Если массив отсортирован, то в зависимости от значения в середине выбирается в какую сторону двигаться.
6. Если ни одно из условий не выполнилось и цикл завершился, то метод возвращает `-1`, что означает, что элемент `target` не найден в массиве.
Этот подход к поиску элемента в отсортированном массиве работает за время O(log n), где n - количество элементов в массиве `nums`.
Условие:
Существует целочисленный массив nums, отсортированный по возрастанию (с разными значениями).
Перед передачей в вашу функцию nums, возможно, поворачивается с неизвестным индексом поворота k (1 <= k < nums.length), так что результирующий массив имеет вид [nums[k], nums[k+1],... , nums[n-1], nums[0], nums[1], ..., nums[k-1]] (с индексом 0). Например, [0,1,2,4,5,6,7] можно повернуть с опорным индексом 3 и превратить в [4,5,6,7,0,1,2].
Учитывая массив nums после возможного поворота и целочисленную цель, верните индекс цели, если он находится в nums, или -1, если он не в nums.
Вы должны написать алгоритм со сложностью выполнения O(log n).
Решение:
class Solution {
public:
int search(vector<int>& nums, int target) {
int left = 0, right = nums.size() - 1;
while (left <= right) {
if (nums[left] == target) {
return left;
} else if (nums[right] == target) {
return right;
}
int m = (left + right) / 2;
if (nums[m] == target) {
return m;
}
if (nums[left] > nums[right]) {
left++, right--;
} else {
if (nums[m] > target) {
right = m - 1, left++;
} else {
left = m + 1, right--;
}
}
}
return -1;
}
};Пояснение:
Данный код реализует функцию `search`, которая выполняет поиск элемента `target` в отсортированном массиве `nums`. В данном случае массив может быть как отсортированным по возрастанию, так и массивом, который был "повернут" (то есть началом может быть какой-то элемент из середины массива).
1. Начальная инициализация левой и правой границ массива.
2. В цикле `while` происходит поиск элемента `target` путем деления текущего отрезка пополам.
3. Если левый, правый или средний элемент равен `target`, то метод возвращает соответствующий индекс.
4. Если массив "повернут", мы уменьшаем правую и увеличиваем левую границу, чтобы сблизиться к участку массива, где нет поворота.
5. Если массив отсортирован, то в зависимости от значения в середине выбирается в какую сторону двигаться.
6. Если ни одно из условий не выполнилось и цикл завершился, то метод возвращает `-1`, что означает, что элемент `target` не найден в массиве.
Этот подход к поиску элемента в отсортированном массиве работает за время O(log n), где n - количество элементов в массиве `nums`.
Задача: 34. Find First and Last Position of Element in Sorted Array #medium
Условие:
Решение:
Пояснение:
Этот код реализует функцию `searchRange`, которая находит диапазон (индексы начала и конца диапазона) в отсортированном списке `nums`, в котором находятся значения `target`. В данном коде используются функции `lower_bound` и `upper_bound` из стандартной библиотеки C++, которые возвращают итераторы на первое и последнее вхождение `target` в отсортированном диапазоне.
1. Создается вектор `ans` для хранения результатов. Изначально вектор заполняется значениями -1, что означает, что значения `target` не найдено в списке.
2. Используя функцию `lower_bound`, находим итератор на первое вхождение `target` в отсортированном диапазоне `nums`.
3. Используя функцию `upper_bound`, находим итератор на элемент, следующий за последним вхождением `target` в отсортированном диапазоне `nums`.
4. Проверяем, действительно ли значение `target` найдено (то есть итератор `l` не указывает на конец вектора и значение по итератору `l` равно `target`).
5. Если значение `target` найдено, мы обновляем диапазон `ans` с индексами начала (взятие расстояния от начала вектора до `l`) и конца (взятие расстояния от начала вектора до `r-1`) вхождения `target`.
6. Возвращаем вектор `ans`.
Этот подход к поиску диапазона работает за время O(log n), где n - количество элементов в массиве `nums`, так как `lower_bound` и `upper_bound` используют бинарный поиск для нахождения элементов.
Условие:
Учитывая массив целых чисел, отсортированных в порядке неубывания, найдите начальную и конечную позицию заданного целевого значения.
Если цель не найдена в массиве, верните [-1, -1].
Вы должны написать алгоритм со сложностью выполнения O(log n).
Решение:
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
vector<int> ans{-1,-1};
auto l = lower_bound(nums.begin(),nums.end(),target);
auto r = upper_bound(nums.begin(),nums.end(),target);
if(l!=nums.end()&&*l==target){
ans[0] = l-nums.begin();
ans[1] = r-nums.begin()-1;
}
return ans;
}
};Пояснение:
Этот код реализует функцию `searchRange`, которая находит диапазон (индексы начала и конца диапазона) в отсортированном списке `nums`, в котором находятся значения `target`. В данном коде используются функции `lower_bound` и `upper_bound` из стандартной библиотеки C++, которые возвращают итераторы на первое и последнее вхождение `target` в отсортированном диапазоне.
1. Создается вектор `ans` для хранения результатов. Изначально вектор заполняется значениями -1, что означает, что значения `target` не найдено в списке.
2. Используя функцию `lower_bound`, находим итератор на первое вхождение `target` в отсортированном диапазоне `nums`.
3. Используя функцию `upper_bound`, находим итератор на элемент, следующий за последним вхождением `target` в отсортированном диапазоне `nums`.
4. Проверяем, действительно ли значение `target` найдено (то есть итератор `l` не указывает на конец вектора и значение по итератору `l` равно `target`).
5. Если значение `target` найдено, мы обновляем диапазон `ans` с индексами начала (взятие расстояния от начала вектора до `l`) и конца (взятие расстояния от начала вектора до `r-1`) вхождения `target`.
6. Возвращаем вектор `ans`.
Этот подход к поиску диапазона работает за время O(log n), где n - количество элементов в массиве `nums`, так как `lower_bound` и `upper_bound` используют бинарный поиск для нахождения элементов.
🔥3
#easy
Задача: 35. Search Insert Position
Дан отсортированный массив уникальных целых чисел и целевое значение. Верните индекс, если цель найдена. Если нет, верните индекс, где она должна быть вставлена в соответствии с порядком.
Вы должны написать алгоритм со временной сложностью O(log n).
Пример:
👨💻 Алгоритм:
1️⃣ Инициализируйте указатели left и right: left = 0, right = n - 1.
2️⃣ Пока left <= right:
Сравните средний элемент массива nums[pivot] с целевым значением target.
Если средний элемент является целевым, то есть target == nums[pivot]: верните pivot.
Если цель не найдена:
Если target < nums[pivot], продолжайте поиск в левом подмассиве. right = pivot - 1.
Иначе продолжайте поиск в правом подмассиве. left = pivot + 1.
3️⃣ Верните left.
😎 Решение:
🪙 434 вопроса вопроса на С/С++ разработчика
🔒 База собесов | 🔒 База тестовых
Задача: 35. Search Insert Position
Дан отсортированный массив уникальных целых чисел и целевое значение. Верните индекс, если цель найдена. Если нет, верните индекс, где она должна быть вставлена в соответствии с порядком.
Вы должны написать алгоритм со временной сложностью O(log n).
Пример:
Input: nums = [1,3,5,6], target = 5
Output: 2
Сравните средний элемент массива nums[pivot] с целевым значением target.
Если средний элемент является целевым, то есть target == nums[pivot]: верните pivot.
Если цель не найдена:
Если target < nums[pivot], продолжайте поиск в левом подмассиве. right = pivot - 1.
Иначе продолжайте поиск в правом подмассиве. left = pivot + 1.
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int pivot, left = 0, right = nums.size() - 1;
while (left <= right) {
pivot = left + (right - left) / 2;
if (nums[pivot] == target) return pivot;
if (target < nums[pivot])
right = pivot - 1;
else
left = pivot + 1;
}
return left;
}
};Please open Telegram to view this post
VIEW IN TELEGRAM
❤1
#medium
Задача: 36. Valid Sudoku
Определите, является ли доска Судоку размером 9 на 9 валидной. Необходимо проверить только заполненные ячейки согласно следующим правилам:
Каждая строка должна содержать цифры от 1 до 9 без повторений.
Каждый столбец должен содержать цифры от 1 до 9 без повторений.
Каждая из девяти подзон размером 3 на 3 в сетке должна содержать цифры от 1 до 9 без повторений.
Пример:
👨💻 Алгоритм:
1️⃣ Инициализируйте список из 9 хеш-множеств, где хеш-множество с индексом r будет использоваться для хранения ранее увиденных чисел в строке r судоку. Аналогично инициализируйте списки из 9 хеш-множеств для отслеживания столбцов и блоков.
2️⃣ Итерируйтесь по каждой позиции (r, c) в судоку. На каждой итерации, если на текущей позиции есть число:
Проверьте, существует ли это число в хеш-множестве для текущей строки, столбца или блока. Если да, верните false, потому что это второе появление числа в текущей строке, столбце или блоке.
3️⃣ В противном случае обновите множество, отвечающее за отслеживание ранее увиденных чисел в текущей строке, столбце и блоке. Индекс текущего блока рассчитывается как (r / 3) * 3 + (c / 3), где / означает деление нацело.
Если дубликаты не были найдены после посещения каждой позиции на доске судоку, то судоку валидно, поэтому верните true.
😎 Решение:
🪙 434 вопроса вопроса на С/С++ разработчика
🔒 База собесов | 🔒 База тестовых
Задача: 36. Valid Sudoku
Определите, является ли доска Судоку размером 9 на 9 валидной. Необходимо проверить только заполненные ячейки согласно следующим правилам:
Каждая строка должна содержать цифры от 1 до 9 без повторений.
Каждый столбец должен содержать цифры от 1 до 9 без повторений.
Каждая из девяти подзон размером 3 на 3 в сетке должна содержать цифры от 1 до 9 без повторений.
Пример:
Input: board =
[["5","3",".",".","7",".",".",".","."]
,["6",".",".","1","9","5",".",".","."]
,[".","9","8",".",".",".",".","6","."]
,["8",".",".",".","6",".",".",".","3"]
,["4",".",".","8",".","3",".",".","1"]
,["7",".",".",".","2",".",".",".","6"]
,[".","6",".",".",".",".","2","8","."]
,[".",".",".","4","1","9",".",".","5"]
,[".",".",".",".","8",".",".","7","9"]]
Output: true
Проверьте, существует ли это число в хеш-множестве для текущей строки, столбца или блока. Если да, верните false, потому что это второе появление числа в текущей строке, столбце или блоке.
Если дубликаты не были найдены после посещения каждой позиции на доске судоку, то судоку валидно, поэтому верните true.
class Solution {
public:
bool isValidSudoku(vector<vector<char>>& board) {
int N = 9;
unordered_set<char> rows[9];
unordered_set<char> cols[9];
unordered_set<char> boxes[9];
for (int r = 0; r < N; r++) {
for (int c = 0; c < N; c++) {
char val = board[r][c];
if (val == '.') {
continue;
}
if (rows[r].find(val) != rows[r].end()) {
return false;
}
rows[r].insert(val);
if (cols[c].find(val) != cols[c].end()) {
return false;
}
cols[c].insert(val);
int idx = (r / 3) * 3 + c / 3;
if (boxes[idx].find(val) != boxes[idx].end()) {
return false;
}
boxes[idx].insert(val);
}
}
return true;
}
};Please open Telegram to view this post
VIEW IN TELEGRAM
#hard
Задача: 37. Sudoku Solver
Напишите программу для решения головоломки Судоку, заполнив пустые ячейки.
Решение Судоку должно удовлетворять всем следующим правилам:
Каждая из цифр от 1 до 9 должна встречаться ровно один раз в каждой строке.
Каждая из цифр от 1 до 9 должна встречаться ровно один раз в каждом столбце.
Каждая из цифр от 1 до 9 должна встречаться ровно один раз в каждом из 9 подблоков 3x3 сетки.
Символ '.' обозначает пустые ячейки.
Пример:
👨💻 Алгоритм:
1️⃣ Теперь все готово для написания функции обратного поиска backtrack(row = 0, col = 0). Начните с верхней левой ячейки row = 0, col = 0. Продолжайте, пока не дойдете до первой свободной ячейки.
2️⃣ Итерируйте по числам от 1 до 9 и попробуйте поставить каждое число d в ячейку (row, col).
Если число d еще не в текущей строке, столбце и блоке:
Поместите d в ячейку (row, col).
Запишите, что d теперь присутствует в текущей строке, столбце и блоке.
3️⃣ Если вы на последней ячейке row == 8, col == 8:
Это означает, что судоку решено.
В противном случае продолжайте размещать дальнейшие числа.
Откат, если решение еще не найдено: удалите последнее число из ячейки (row, col).
😎 Решение:
🪙 434 вопроса вопроса на С/С++ разработчика
🔒 База собесов | 🔒 База тестовых
Задача: 37. Sudoku Solver
Напишите программу для решения головоломки Судоку, заполнив пустые ячейки.
Решение Судоку должно удовлетворять всем следующим правилам:
Каждая из цифр от 1 до 9 должна встречаться ровно один раз в каждой строке.
Каждая из цифр от 1 до 9 должна встречаться ровно один раз в каждом столбце.
Каждая из цифр от 1 до 9 должна встречаться ровно один раз в каждом из 9 подблоков 3x3 сетки.
Символ '.' обозначает пустые ячейки.
Пример:
Input: board =
[["5","3",".",".","7",".",".",".","."],
["6",".",".","1","9","5",".",".","."],
[".","9","8",".",".",".",".","6","."],
["8",".",".",".","6",".",".",".","3"],
["4",".",".","8",".","3",".",".","1"],
["7",".",".",".","2",".",".",".","6"],
[".","6",".",".",".",".","2","8","."],
[".",".",".","4","1","9",".",".","5"],
[".",".",".",".","8",".",".","7","9"]]
Output:
[["5","3","4","6","7","8","9","1","2"],["6","7","2","1","9","5","3","4","8"],["1","9","8","3","4","2","5","6","7"],["8","5","9","7","6","1","4","2","3"],["4","2","6","8","5","3","7","9","1"],["7","1","3","9","2","4","8","5","6"],["9","6","1","5","3","7","2","8","4"],["2","8","7","4","1","9","6","3","5"],["3","4","5","2","8","6","1","7","9"]]
Если число d еще не в текущей строке, столбце и блоке:
Поместите d в ячейку (row, col).
Запишите, что d теперь присутствует в текущей строке, столбце и блоке.
Это означает, что судоку решено.
В противном случае продолжайте размещать дальнейшие числа.
Откат, если решение еще не найдено: удалите последнее число из ячейки (row, col).
#include <vector>
class Solution {
int n = 3;
int N = n * n;
std::vector<std::vector<int>> rows, cols, boxes;
std::vector<std::vector<char>> board;
bool sudoku_solved = false;
public:
Solution() : rows(N, std::vector<int>(N + 1, 0)), cols(N, std::vector<int>(N + 1, 0)), boxes(N, std::vector<int>(N + 1, 0)) {}
bool could_place(int d, int row, int col) {
int idx = (row / n) * n + col / n;
return rows[row][d] == 0 && cols[col][d] == 0 && boxes[idx][d] == 0;
}
void place_or_remove(int d, int row, int col, bool place) {
int idx = (row / n) * n + col / n;
int delta = place ? 1 : -1;
rows[row][d] += delta;
cols[col][d] += delta;
boxes[idx][d] += delta;
board[row][col] = place ? d + '0' : '.';
}
void backtrack(int row = 0, int col = 0) {
if (col == N) col = 0, row++;
if (row == N) {
sudoku_solved = true;
return;
}
if (board[row][col] == '.') {
for (int d = 1; d <= 9 && !sudoku_solved; d++) {
if (could_place(d, row, col)) {
place_or_remove(d, row, col, true);
backtrack(row, col + 1);
if (!sudoku_solved) place_or_remove(d, row, col, false);
}
}
} else
backtrack(row, col + 1);
}
void solveSudoku(std::vector<std::vector<char>>& in_board) {
board = in_board;
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
if (board[i][j] != '.') {
int d = board[i][j] - '0';
place_or_remove(d, i, j, true);
}
}
}
backtrack();
in_board = board;
}
};
Please open Telegram to view this post
VIEW IN TELEGRAM
#medium
Задача: 38. Count and Say
Последовательность "считай и скажи" — это последовательность строк цифр, определяемая с помощью рекурсивной формулы:
countAndSay(1) = "1"
countAndSay(n) — это кодирование длин серий из countAndSay(n - 1).
Кодирование длин серий (RLE) — это метод сжатия строк, который работает путём замены последовательных идентичных символов (повторяющихся 2 или более раз) на конкатенацию символа и числа, обозначающего количество символов (длину серии). Например, чтобы сжать строку "3322251", мы заменяем "33" на "23", "222" на "32", "5" на "15" и "1" на "11". Таким образом, сжатая строка становится "23321511".
Для заданного положительного целого числа n верните n-й элемент последовательности "считай и скажи".
Пример:
👨💻 Алгоритм:
1️⃣ Мы хотим использовать шаблон, который соответствует строкам из одинаковых символов, таких как "4", "7777", "2222222".
Если у вас есть опыт работы с регулярными выражениями, вы можете обнаружить, что шаблон (.)\1* работает.
2️⃣ Мы можем разбить это регулярное выражение на три части:
(.): оно определяет группу, содержащую один символ, который может быть чем угодно.
3️⃣ *: этот квалификатор, следующий за ссылкой на группу \1, указывает, что мы хотели бы видеть повторение группы ноль или более раз.
Таким образом, шаблон соответствует строкам, которые состоят из некоторого символа, а затем ноль или более повторений этого символа после его первого появления. Это то, что нам нужно.
Мы находим все совпадения с регулярным выражением, а затем конкатенируем результаты.
😎 Решение:
🪙 434 вопроса вопроса на С/С++ разработчика
🔒 База собесов | 🔒 База тестовых
Задача: 38. Count and Say
Последовательность "считай и скажи" — это последовательность строк цифр, определяемая с помощью рекурсивной формулы:
countAndSay(1) = "1"
countAndSay(n) — это кодирование длин серий из countAndSay(n - 1).
Кодирование длин серий (RLE) — это метод сжатия строк, который работает путём замены последовательных идентичных символов (повторяющихся 2 или более раз) на конкатенацию символа и числа, обозначающего количество символов (длину серии). Например, чтобы сжать строку "3322251", мы заменяем "33" на "23", "222" на "32", "5" на "15" и "1" на "11". Таким образом, сжатая строка становится "23321511".
Для заданного положительного целого числа n верните n-й элемент последовательности "считай и скажи".
Пример:
Input: n = 4
Output: "1211"
Explanation:
countAndSay(1) = "1"
countAndSay(2) = RLE of "1" = "11"
countAndSay(3) = RLE of "11" = "21"
countAndSay(4) = RLE of "21" = "1211"
Если у вас есть опыт работы с регулярными выражениями, вы можете обнаружить, что шаблон (.)\1* работает.
(.): оно определяет группу, содержащую один символ, который может быть чем угодно.
Таким образом, шаблон соответствует строкам, которые состоят из некоторого символа, а затем ноль или более повторений этого символа после его первого появления. Это то, что нам нужно.
Мы находим все совпадения с регулярным выражением, а затем конкатенируем результаты.
class Solution {
public:
string countAndSay(int n) {
regex e("(.)\\1*");
string s = "1";
for (int i = 2; i <= n; i++) {
string t;
for (sregex_iterator it = sregex_iterator(s.begin(), s.end(), e);
it != sregex_iterator(); it++) {
t += to_string(it->str().size()) + it->str(1);
}
s = t;
}
return s;
}
};Please open Telegram to view this post
VIEW IN TELEGRAM
❤1👍1🔥1
#medium
Задача: 39. Combination Sum
Дан массив уникальных целых чисел candidates и целевое целое число target. Верните список всех уникальных комбинаций из candidates, где выбранные числа в сумме дают target. Комбинации можно возвращать в любом порядке.
Одно и то же число может быть выбрано из массива candidates неограниченное количество раз. Две комбинации считаются уникальными, если частота хотя бы одного из выбранных чисел отличается.
Тестовые случаи сгенерированы таким образом, что количество уникальных комбинаций, дающих в сумме target, меньше 150 комбинаций для данного ввода.
Пример:
👨💻 Алгоритм:
1️⃣ Как видно, вышеописанный алгоритм обратного отслеживания разворачивается как обход дерева в глубину (DFS - Depth-First Search), который часто реализуется с помощью рекурсии.
Здесь мы определяем рекурсивную функцию backtrack(remain, comb, start) (на Python), которая заполняет комбинации, начиная с текущей комбинации (comb), оставшейся суммы для выполнения (remain) и текущего курсора (start) в списке кандидатов.
Следует отметить, что сигнатура рекурсивной функции немного отличается в Java, но идея остается той же.
2️⃣ Для первого базового случая рекурсивной функции, если remain == 0, то есть мы достигаем желаемой целевой суммы, поэтому мы можем добавить текущую комбинацию в итоговый список.
Как другой базовый случай, если remain < 0, то есть мы превышаем целевое значение, мы прекращаем исследование на этом этапе.
3️⃣ Помимо вышеупомянутых двух базовых случаев, мы затем продолжаем исследовать подсписок кандидатов, начиная с [start ... n].
Для каждого из кандидатов мы вызываем рекурсивную функцию саму с обновленными параметрами.
Конкретно, мы добавляем текущего кандидата в комбинацию.
С добавленным кандидатом у нас теперь меньше суммы для выполнения, то есть remain - candidate.
Для следующего исследования мы все еще начинаем с текущего курсора start.
В конце каждого исследования мы делаем откат, удаляя кандидата из комбинации.
😎 Решение:
🪙 434 вопроса вопроса на С/С++ разработчика
🔒 База собесов | 🔒 База тестовых
Задача: 39. Combination Sum
Дан массив уникальных целых чисел candidates и целевое целое число target. Верните список всех уникальных комбинаций из candidates, где выбранные числа в сумме дают target. Комбинации можно возвращать в любом порядке.
Одно и то же число может быть выбрано из массива candidates неограниченное количество раз. Две комбинации считаются уникальными, если частота хотя бы одного из выбранных чисел отличается.
Тестовые случаи сгенерированы таким образом, что количество уникальных комбинаций, дающих в сумме target, меньше 150 комбинаций для данного ввода.
Пример:
Input: candidates = [2,3,5], target = 8
Output: [[2,2,2,2],[2,3,3],[3,5]]
Здесь мы определяем рекурсивную функцию backtrack(remain, comb, start) (на Python), которая заполняет комбинации, начиная с текущей комбинации (comb), оставшейся суммы для выполнения (remain) и текущего курсора (start) в списке кандидатов.
Следует отметить, что сигнатура рекурсивной функции немного отличается в Java, но идея остается той же.
Как другой базовый случай, если remain < 0, то есть мы превышаем целевое значение, мы прекращаем исследование на этом этапе.
Для каждого из кандидатов мы вызываем рекурсивную функцию саму с обновленными параметрами.
Конкретно, мы добавляем текущего кандидата в комбинацию.
С добавленным кандидатом у нас теперь меньше суммы для выполнения, то есть remain - candidate.
Для следующего исследования мы все еще начинаем с текущего курсора start.
В конце каждого исследования мы делаем откат, удаляя кандидата из комбинации.
class Solution {
public:
void backtrack(int remain, vector<int>& comb, int start,
const vector<int>& candidates,
vector<vector<int>>& results) {
if (remain == 0) {
results.push_back(comb);
return;
} else if (remain < 0) {
return;
}
for (int i = start; i < candidates.size(); ++i) {
comb.push_back(candidates[i]);
backtrack(remain - candidates[i], comb, i, candidates, results);
comb.pop_back();
}
}
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
vector<vector<int>> results;
vector<int> comb;
backtrack(target, comb, 0, candidates, results);
return results;
}
};Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
#medium
Задача: 40. Combination Sum II
Дана коллекция кандидатов (candidates) и целевое число (target). Найдите все уникальные комбинации в candidates, где числа кандидатов в сумме дают target.
Каждое число в candidates может быть использовано только один раз в комбинации.
Примечание: Набор решений не должен содержать повторяющихся комбинаций.
Пример:
👨💻 Алгоритм:
1️⃣ Во-первых, мы создаём таблицу счётчиков из предоставленного списка чисел. Затем мы используем эту таблицу счётчиков в процессе обратного поиска, который мы определяем как функцию
2️⃣ При каждом вызове функции обратного поиска мы сначала проверяем, достигли ли мы целевой суммы (то есть
3️⃣ Если осталась сумма для заполнения, мы затем перебираем текущую таблицу счётчиков, чтобы выбрать следующего кандидата. После выбора кандидата мы продолжаем исследование, вызывая функцию
😎 Решение:
🪙 434 вопроса вопроса на С/С++ разработчика
🔒 База собесов | 🔒 База тестовых
Задача: 40. Combination Sum II
Дана коллекция кандидатов (candidates) и целевое число (target). Найдите все уникальные комбинации в candidates, где числа кандидатов в сумме дают target.
Каждое число в candidates может быть использовано только один раз в комбинации.
Примечание: Набор решений не должен содержать повторяющихся комбинаций.
Пример:
Input: candidates = [10,1,2,7,6,1,5], target = 8
Output:
[
[1,1,6],
[1,2,5],
[1,7],
[2,6]
]
backtrack(comb, remain, curr, candidate_groups, results). Для сохранения состояния на каждом этапе обратного поиска мы используем несколько параметров в функции:comb: комбинация, которую мы построили на данный момент.remain: оставшаяся сумма, которую нам нужно заполнить, чтобы достичь целевой суммы.curr: курсор, который указывает на текущую группу чисел, используемую из таблицы счётчиков.counter: текущая таблица счётчиков.results: окончательные комбинации, которые достигают целевой суммы.sum(comb) = target), и нужно ли прекратить исследование, потому что сумма текущей комбинации превышает желаемую целевую сумму.backtrack() с обновлёнными состояниями. Более важно, что в конце каждого исследования нам нужно вернуть состояние, которое мы обновили ранее, чтобы начать с чистого листа для следующего исследования. Именно из-за этой операции обратного поиска алгоритм получил своё название.class Solution {
public:
vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
vector<vector<int>> results;
vector<int> comb;
map<int, int> counter;
for (int candidate : candidates) {
if (counter.find(candidate) != counter.end())
counter[candidate] += 1;
else
counter[candidate] = 1;
}
vector<pair<int, int>> counterList(counter.begin(), counter.end());
backtrack(comb, target, 0, counterList, results);
return results;
}
private:
void backtrack(vector<int>& comb, int remain, int curr,
vector<pair<int, int>>& counter,
vector<vector<int>>& results) {
if (remain <= 0) {
if (remain == 0)
results.push_back(vector<int>(comb.begin(), comb.end()));
return;
}
for (int nextCurr = curr; nextCurr < counter.size(); ++nextCurr) {
pair<int, int>& entry = counter[nextCurr];
int candidate = entry.first;
int& freq = entry.second;
if (freq <= 0) continue;
comb.push_back(candidate);
--freq;
backtrack(comb, remain - candidate, nextCurr, counter, results);
++freq;
comb.pop_back();
}
}
};Please open Telegram to view this post
VIEW IN TELEGRAM
❤1👍1
#hard
Задача: 41. First Missing Positive
Дан неотсортированный массив целых чисел nums. Верните наименьшее положительное целое число, которого нет в массиве nums.
Необходимо реализовать алгоритм, который работает за время O(n) и использует O(1) дополнительной памяти.
Пример:
👨💻 Алгоритм:
1️⃣ Инициализировать переменную n длиной массива nums. Создать массив seen размером n + 1. Отметить элементы в массиве nums как просмотренные в массиве seen.
Для каждого числа num в массиве nums, если num больше 0 и меньше или равно n, установить seen[num] в значение true.
2️⃣ Найти наименьшее недостающее положительное число:
Проитерировать от 1 до n, и если seen[i] не равно true, вернуть i как наименьшее недостающее положительное число.
3️⃣ Если массив seen содержит все элементы от 1 до n, вернуть n + 1 как наименьшее недостающее положительное число.
😎 Решение:
🪙 434 вопроса вопроса на С/С++ разработчика
🔒 База собесов | 🔒 База тестовых
Задача: 41. First Missing Positive
Дан неотсортированный массив целых чисел nums. Верните наименьшее положительное целое число, которого нет в массиве nums.
Необходимо реализовать алгоритм, который работает за время O(n) и использует O(1) дополнительной памяти.
Пример:
Input: nums = [3,4,-1,1]
Output: 2
Explanation: 1 is in the array but 2 is missing.
Для каждого числа num в массиве nums, если num больше 0 и меньше или равно n, установить seen[num] в значение true.
Проитерировать от 1 до n, и если seen[i] не равно true, вернуть i как наименьшее недостающее положительное число.
int firstMissingPositive(std::vector<int>& nums) {
for (int i = 0; i < nums.size(); ){
if (nums[i] <= 0 || nums[i] > nums.size()) {
++i;
continue;
}
if (nums[i] == i + 1) {
++i;
continue;
}
int j = nums[i];
if (nums[j-1] == nums[i]) {
++i;
continue;
}
std::swap(nums[j-1], nums[i]);
}
for (int i = 0; i < nums.size(); ++i) {
if (i != (nums[i] - 1) ) {
return i + 1;
}
}
return nums.size() + 1;
}Please open Telegram to view this post
VIEW IN TELEGRAM
❤2💊1
#hard
Задача: 42. Trapping Rain Water
Дано n неотрицательных целых чисел, представляющих карту высот, где ширина каждого столбца равна 1. Вычислите, сколько воды он может удержать после дождя.
Пример:
👨💻 Алгоритм:
1️⃣ Найдите максимальную высоту столбца с левого конца до индекса i в массиве left_max.
2️⃣ Найдите максимальную высоту столбца с правого конца до индекса i в массиве right_max.
3️⃣ Итерируйте по массиву высот height и обновляйте ans: добавьте min(left_max[i], right_max[i]) - height[i] к ans.
😎 Решение:
🔥 ТОП ВОПРОСОВ С СОБЕСОВ
🔒 База собесов | 🔒 База тестовых
Задача: 42. Trapping Rain Water
Дано n неотрицательных целых чисел, представляющих карту высот, где ширина каждого столбца равна 1. Вычислите, сколько воды он может удержать после дождя.
Пример:
Input: height = [0,1,0,2,1,0,1,3,2,1,2,1]
Output: 6
class Solution {
public:
int trap(vector<int>& height) {
int left = 0, right = height.size() - 1;
int ans = 0;
int left_max = 0, right_max = 0;
while (left < right) {
if (height[left] < height[right]) {
height[left] >= left_max ? (left_max = height[left])
: ans += (left_max - height[left]);
++left;
} else {
height[right] >= right_max ? (right_max = height[right])
: ans += (right_max - height[right]);
--right;
}
}
return ans;
}
};Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
#hard
Задача: 43. Multiply Strings
Даны два неотрицательных целых числа num1 и num2, представленные в виде строк. Верните произведение num1 и num2, также представленное в виде строки.
Примечание: Вы не должны использовать встроенную библиотеку BigInteger или прямо преобразовывать входные данные в целые числа.
Пример:
👨💻 Алгоритм:
1️⃣ Переверните оба числа. Инициализируйте массив ans с (N+M) нулями. Для каждой цифры в secondNumber:
Инициализируйте переменную carry, первоначально равную 0.
Инициализируйте массив (currentResult), который начинается с некоторого количества нулей, основываясь на позиции цифры в secondNumber.
2️⃣ Для каждой цифры в firstNumber:
Умножьте цифру из secondNumber на цифру из firstNumber и добавьте предыдущий carry к умножению.
Возьмите остаток от деления умножения на 10, чтобы получить последнюю цифру.
Добавьте последнюю цифру в массив currentResult.
Разделите умножение на 10, чтобы получить новое значение для carry.
3️⃣ После итерации по каждой цифре в первом числе, если carry не равен нулю, добавьте carry в currentResult.
Добавьте currentResult к ans.
Если последняя цифра в ans равна нулю, перед тем как перевернуть ans, необходимо удалить ноль из ans. В противном случае в финальном ответе будет ведущий ноль.
Переверните ans и верните его.
😎 Решение:
🔥 ТОП ВОПРОСОВ С СОБЕСОВ
🔒 База собесов | 🔒 База тестовых
Задача: 43. Multiply Strings
Даны два неотрицательных целых числа num1 и num2, представленные в виде строк. Верните произведение num1 и num2, также представленное в виде строки.
Примечание: Вы не должны использовать встроенную библиотеку BigInteger или прямо преобразовывать входные данные в целые числа.
Пример:
Input: num1 = "2", num2 = "3"
Output: "6"
Инициализируйте переменную carry, первоначально равную 0.
Инициализируйте массив (currentResult), который начинается с некоторого количества нулей, основываясь на позиции цифры в secondNumber.
Умножьте цифру из secondNumber на цифру из firstNumber и добавьте предыдущий carry к умножению.
Возьмите остаток от деления умножения на 10, чтобы получить последнюю цифру.
Добавьте последнюю цифру в массив currentResult.
Разделите умножение на 10, чтобы получить новое значение для carry.
Добавьте currentResult к ans.
Если последняя цифра в ans равна нулю, перед тем как перевернуть ans, необходимо удалить ноль из ans. В противном случае в финальном ответе будет ведущий ноль.
Переверните ans и верните его.
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
#hard
Задача: 44. Wildcard Matching
Дана входная строка (s) и шаблон (p), реализуйте сопоставление с шаблоном с поддержкой символов '?' и '*' где:
'?' соответствует любому одиночному символу.
'*' соответствует любой последовательности символов (включая пустую последовательность).
Сопоставление должно покрывать всю входную строку (не частично).
Пример:
👨💻 Алгоритм:
1️⃣ Очистите входные данные, заменив несколько звездочек подряд одной звездочкой: p = remove_duplicate_stars(p).
Инициируйте хеш-карту для мемоизации dp.
Верните вспомогательную функцию с очищенным входом: helper(s, p).
2️⃣ Функция helper(s, p):
Если пара (s, p) уже известна и сохранена в dp, верните значение.
Если строки равны (p == s) или шаблон соответствует любой строке (p == '*'), добавьте dp[(s, p)] = True.
В противном случае, если p пусто или s пусто, добавьте dp[(s, p)] = False.
Если текущие символы совпадают (p[0] == s[0] или p[0] == '?'), то сравните следующие и добавьте dp[(s, p)] = helper(s[1:], p[1:]).
3️⃣ Если текущий символ шаблона - звездочка (p[0] == '*'), то возможны две ситуации: звездочка не соответствует никаким символам, и звездочка соответствует одному или нескольким символам: dp[(s, p)] = helper(s, p[1:]) или helper(s[1:], p).
Если p[0] != s[0], тогда добавьте dp[(s, p)] = False.
Когда значение вычислено, верните его: dp[(s, p)].
😎 Решение:
🔥 ТОП ВОПРОСОВ С СОБЕСОВ
🔒 База собесов | 🔒 База тестовых
Задача: 44. Wildcard Matching
Дана входная строка (s) и шаблон (p), реализуйте сопоставление с шаблоном с поддержкой символов '?' и '*' где:
'?' соответствует любому одиночному символу.
'*' соответствует любой последовательности символов (включая пустую последовательность).
Сопоставление должно покрывать всю входную строку (не частично).
Пример:
Input: s = "aa", p = "a"
Output: false
Explanation: "a" does not match the entire string "aa".
Инициируйте хеш-карту для мемоизации dp.
Верните вспомогательную функцию с очищенным входом: helper(s, p).
Если пара (s, p) уже известна и сохранена в dp, верните значение.
Если строки равны (p == s) или шаблон соответствует любой строке (p == '*'), добавьте dp[(s, p)] = True.
В противном случае, если p пусто или s пусто, добавьте dp[(s, p)] = False.
Если текущие символы совпадают (p[0] == s[0] или p[0] == '?'), то сравните следующие и добавьте dp[(s, p)] = helper(s[1:], p[1:]).
Если p[0] != s[0], тогда добавьте dp[(s, p)] = False.
Когда значение вычислено, верните его: dp[(s, p)].
class Solution {
public:
unordered_map<string, bool> dp;
string p;
string s;
string remove_duplicate_stars(string p) {
string new_string = "";
for (auto &c : p) {
if (new_string.empty() || c != '*')
new_string += c;
else if (new_string.back() != '*')
new_string += c;
}
return new_string;
}
bool helper(int si, int pi) {
string key = to_string(si) + "," + to_string(pi);
if (dp.count(key)) return dp[key];
if (pi == p.size())
dp[key] = (si == s.size());
else if (si == s.size())
dp[key] = (pi + 1 == p.size() && p[pi] == '*');
else if (p[pi] == s[si] || p[pi] == '?')
dp[key] = helper(si + 1, pi + 1);
else if (p[pi] == '*')
dp[key] = helper(si, pi + 1) || helper(si + 1, pi);
else
dp[key] = false;
return dp[key];
}
bool isMatch(string s, string p) {
dp.clear();
this->s = s;
this->p = remove_duplicate_stars(p);
return helper(0, 0);
}
};Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
#medium
Задача: 45. Jump Game II
Вам дан массив целых чисел nums с индексацией с нуля и длиной n. Изначально вы находитесь в позиции nums[0].
Каждый элемент nums[i] представляет максимальную длину прыжка вперед с индекса i. Другими словами, если вы находитесь в nums[i], вы можете прыгнуть на любой nums[i + j], где:
0 <= j <= nums[i] и
i + j < n
Верните минимальное количество прыжков, чтобы достичь nums[n - 1]. Тестовые случаи сгенерированы таким образом, что вы можете достичь nums[n - 1].
Пример:
👨💻 Алгоритм:
1️⃣ Инициализируйте curEnd = 0, curFar = 0 и количество прыжков как answer = 0.
2️⃣ Итерируйтесь по массиву nums. Для каждого индекса i наибольший индекс, до которого мы можем добраться из i, равен i + nums[i]. Обновите curFar = max(curFar, i + nums[i]).
3️⃣ Если i = curEnd, это означает, что текущий прыжок завершен, и следует перейти к следующему прыжку. Увеличьте answer и установите curEnd = curFar как наибольшее расстояние, которое мы можем преодолеть следующим прыжком. Повторите с шага 2.
😎 Решение:
🔥 ТОП ВОПРОСОВ С СОБЕСОВ
🔒 База собесов | 🔒 База тестовых
Задача: 45. Jump Game II
Вам дан массив целых чисел nums с индексацией с нуля и длиной n. Изначально вы находитесь в позиции nums[0].
Каждый элемент nums[i] представляет максимальную длину прыжка вперед с индекса i. Другими словами, если вы находитесь в nums[i], вы можете прыгнуть на любой nums[i + j], где:
0 <= j <= nums[i] и
i + j < n
Верните минимальное количество прыжков, чтобы достичь nums[n - 1]. Тестовые случаи сгенерированы таким образом, что вы можете достичь nums[n - 1].
Пример:
Input: nums = [2,3,0,1,4]
Output: 2
class Solution {
public:
int jump(vector<int>& nums) {
int answer = 0, n = int(nums.size());
int curEnd = 0, curFar = 0;
for (int i = 0; i < n - 1; ++i) {
curFar = max(curFar, i + nums[i]);
if (i == curEnd) {
answer++;
curEnd = curFar;
}
}
return answer;
}
};Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
#medium
Задача: 46. Permutations
Дан массив nums, состоящий из различных целых чисел, верните все возможные перестановки. Вы можете вернуть ответ в любом порядке.
Пример:
👨💻 Алгоритм:
1️⃣ Если длина curr равна длине nums, добавьте копию curr в ans и вернитесь.
2️⃣ Итерируйтесь по nums. Для каждого num, если num не в curr, выполните следующее:
Добавьте num в curr и вызовите backtrack(curr), затем удалите num из curr.
3️⃣ Вызовите backtrack с изначально пустым curr.
Верните ans.
😎 Решение:
🔥 ТОП ВОПРОСОВ С СОБЕСОВ
🔒 База собесов | 🔒 База тестовых
Задача: 46. Permutations
Дан массив nums, состоящий из различных целых чисел, верните все возможные перестановки. Вы можете вернуть ответ в любом порядке.
Пример:
Input: nums = [0,1]
Output: [[0,1],[1,0]]
Добавьте num в curr и вызовите backtrack(curr), затем удалите num из curr.
Верните ans.
class Solution {
public:
vector<vector<int>> permute(vector<int>& nums) {
vector<vector<int>> ans;
vector<int> curr = {};
backtrack(curr, ans, nums);
return ans;
}
void backtrack(vector<int>& curr, vector<vector<int>>& ans,
vector<int>& nums) {
if (curr.size() == nums.size()) {
ans.push_back(curr);
return;
}
for (int num : nums) {
if (find(curr.begin(), curr.end(), num) == curr.end()) {
curr.push_back(num);
backtrack(curr, ans, nums);
curr.pop_back();
}
}
}
};Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3
#medium
Задача: 55. Jump Game
Вам дан массив целых чисел nums. Изначально вы находитесь на первом индексе массива, и каждый элемент массива представляет вашу максимальную длину прыжка в этой позиции.
Верните true, если вы можете достичь последнего индекса, или false в противном случае.
Пример:
👨💻 Алгоритм:
1️⃣ Инициализация таблицы памяти:
Изначально все элементы таблицы памяти имеют статус UNKNOWN, за исключением последнего, который является (тривиально) GOOD (может достичь сам себя).
2️⃣ Модификация алгоритма обратного трассирования:
Измените алгоритм обратного трассирования таким образом, чтобы на рекурсивном шаге сначала проверялось, известен ли индекс (GOOD/BAD).
Если индекс известен, тогда возвращается True/False.
3️⃣ Выполнение и сохранение результатов:
Если индекс не известен, выполняйте шаги обратного трассирования, как ранее.
После определения значения текущего индекса, сохраните его в таблице памяти.
😎 Решение:
🔥 ТОП ВОПРОСОВ С СОБЕСОВ
🔒 База собесов | 🔒 База тестовых
Задача: 55. Jump Game
Вам дан массив целых чисел nums. Изначально вы находитесь на первом индексе массива, и каждый элемент массива представляет вашу максимальную длину прыжка в этой позиции.
Верните true, если вы можете достичь последнего индекса, или false в противном случае.
Пример:
Input: nums = [2,3,1,1,4]
Output: true
Explanation: Jump 1 step from index 0 to 1, then 3 steps to the last index.
Изначально все элементы таблицы памяти имеют статус UNKNOWN, за исключением последнего, который является (тривиально) GOOD (может достичь сам себя).
Измените алгоритм обратного трассирования таким образом, чтобы на рекурсивном шаге сначала проверялось, известен ли индекс (GOOD/BAD).
Если индекс известен, тогда возвращается True/False.
Если индекс не известен, выполняйте шаги обратного трассирования, как ранее.
После определения значения текущего индекса, сохраните его в таблице памяти.
enum Index { GOOD, BAD, UNKNOWN };
class Solution {
public:
vector<Index> memo;
bool canJumpFromPosition(int position, vector<int>& nums) {
if (memo[position] != UNKNOWN) {
return memo[position] == GOOD;
}
int furthestJump = min(position + nums[position], (int)nums.size() - 1);
for (int nextPosition = position + 1; nextPosition <= furthestJump; nextPosition++) {
if (canJumpFromPosition(nextPosition, nums)) {
memo[position] = GOOD;
return true;
}
}
memo[position] = BAD;
return false;
}
bool canJump(vector<int>& nums) {
memo = vector<Index>(nums.size(), UNKNOWN);
memo[memo.size() - 1] = GOOD;
return canJumpFromPosition(0, nums);
}
};Please open Telegram to view this post
VIEW IN TELEGRAM
❤1
#medium
Задача: 56. Merge Intervals
Дан массив интервалов, где intervals[i] = [starti, endi]. Объедините все перекрывающиеся интервалы и верните массив неперекрывающихся интервалов, которые покрывают все интервалы во входных данных.
Пример:
👨💻 Алгоритм:
1️⃣ Представление графа:
Имея представленную интуицию, мы можем изобразить граф в виде списка смежности, вставляя направленные ребра в обоих направлениях, чтобы симулировать неориентированные ребра.
2️⃣ Определение компонент связности:
Для определения, в какой компоненте связности находится каждый узел, мы выполняем обходы графа от произвольных непосещенных узлов до тех пор, пока все узлы не будут посещены. Для эффективности мы храним посещенные узлы в множестве (Set), что позволяет проводить проверки на принадлежность и вставку за константное время.
3️⃣ Объединение интервалов внутри компонент:
Наконец, мы рассматриваем каждую связную компоненту, объединяя все её интервалы, создавая новый интервал с началом, равным минимальному началу среди всех интервалов в компоненте, и концом, равным максимальному концу.
Решение:
🔥 ТОП ВОПРОСОВ С СОБЕСОВ
🔒 База собесов | 🔒 База тестовых
Задача: 56. Merge Intervals
Дан массив интервалов, где intervals[i] = [starti, endi]. Объедините все перекрывающиеся интервалы и верните массив неперекрывающихся интервалов, которые покрывают все интервалы во входных данных.
Пример:
Input: intervals = [[1,3],[2,6],[8,10],[15,18]]
Output: [[1,6],[8,10],[15,18]]
Explanation: Since intervals [1,3] and [2,6] overlap, merge them into [1,6].
Имея представленную интуицию, мы можем изобразить граф в виде списка смежности, вставляя направленные ребра в обоих направлениях, чтобы симулировать неориентированные ребра.
Для определения, в какой компоненте связности находится каждый узел, мы выполняем обходы графа от произвольных непосещенных узлов до тех пор, пока все узлы не будут посещены. Для эффективности мы храним посещенные узлы в множестве (Set), что позволяет проводить проверки на принадлежность и вставку за константное время.
Наконец, мы рассматриваем каждую связную компоненту, объединяя все её интервалы, создавая новый интервал с началом, равным минимальному началу среди всех интервалов в компоненте, и концом, равным максимальному концу.
Решение:
#include <vector>
#include <map>
#include <set>
#include <stack>
using namespace std;
class Solution {
public:
map<vector<int>, vector<vector<int>>> graph;
map<int, vector<vector<int>>> nodes_in_comp;
set<vector<int>> visited;
bool overlap(vector<int>& a, vector<int>& b) {
return a[0] <= b[1] && b[0] <= a[1];
}
void buildGraph(vector<vector<int>>& intervals) {
for (auto interval1 : intervals) {
for (auto interval2 : intervals) {
if (overlap(interval1, interval2)) {
graph[interval1].push_back(interval2);
graph[interval2].push_back(interval1);
}
}
}
}
vector<int> mergeNodes(vector<vector<int>>& nodes) {
int min_start = nodes[0][0];
for (auto node : nodes) {
min_start = min(min_start, node[0]);
}
int max_end = nodes[0][1];
for (auto node : nodes) {
max_end = max(max_end, node[1]);
}
return {min_start, max_end};
}
void markComponentDFS(vector<int>& start, int comp_number) {
stack<vector<int>> stk;
stk.push(start);
while (!stk.empty()) {
vector<int> node = stk.top();
stk.pop();
if (visited.find(node) == visited.end()) {
visited.insert(node);
nodes_in_comp[comp_number].push_back(node);
for (auto child : graph[node]) {
stk.push(child);
}
}
}
}
void buildComponents(vector<vector<int>>& intervals) {
int comp_number = 0;
for (auto interval : intervals) {
if (visited.find(interval) == visited.end()) {
markComponentDFS(interval, comp_number);
comp_number++;
}
}
}
vector<vector<int>> merge(vector<vector<int>>& intervals) {
buildGraph(intervals);
buildComponents(intervals);
vector<vector<int>> merged;
for (size_t comp = 0; comp < nodes_in_comp.size(); comp++) {
merged.push_back(mergeNodes(nodes_in_comp[comp]));
}
return merged;
}
};
Please open Telegram to view this post
VIEW IN TELEGRAM
❤4🤔1💊1
#medium
Задача: 57. Insert Interval
Вам дан массив непересекающихся интервалов
Вставьте
Верните массив
Обратите внимание, что не обязательно модифицировать массив
Пример:
👨💻 Алгоритм:
1️⃣ Инициализация переменных:
Инициализируются переменные n и i для хранения размера массива интервалов и текущего индекса соответственно, а также пустой массив res для хранения результата.
2️⃣ Обработка случаев без перекрытия и с перекрытием:
В случае отсутствия перекрытия до вставки, проходим через массив интервалов до тех пор, пока конечная точка текущего интервала меньше начальной точки нового интервала. Добавляем текущий интервал в массив res и переходим к следующему.
В случае перекрытия, продолжаем обход, пока начальная точка нового интервала меньше или равна конечной точке текущего интервала. Обновляем начальные и конечные точки нового интервала, объединяя перекрывающиеся интервалы в один.
3️⃣ Обработка интервалов после вставки:
Проходим через оставшиеся интервалы после индекса i и добавляем их в массив res. Это включает интервалы, которые следуют после нового интервала и не перекрываются с ним.
Возвращаем массив res, содержащий все интервалы с корректно вставленным новым интервалом.
😎 Решение:
🔥 ТОП ВОПРОСОВ С СОБЕСОВ
🔒 База собесов | 🔒 База тестовых
Задача: 57. Insert Interval
Вам дан массив непересекающихся интервалов
intervals, где intervals[i] = [starti, endi] представляет начало и конец i-го интервала, и массив intervals отсортирован в порядке возрастания по starti. Вам также дан интервал newInterval = [start, end], представляющий начало и конец другого интервала.Вставьте
newInterval в массив intervals так, чтобы intervals оставался отсортированным в порядке возрастания по starti и в intervals не было бы перекрывающихся интервалов (при необходимости объедините перекрывающиеся интервалы).Верните массив
intervals после вставки.Обратите внимание, что не обязательно модифицировать массив
intervals на месте. Вы можете создать новый массив и вернуть его.Пример:
Input: intervals = [[1,3],[6,9]], newInterval = [2,5]
Output: [[1,5],[6,9]]
Инициализируются переменные n и i для хранения размера массива интервалов и текущего индекса соответственно, а также пустой массив res для хранения результата.
В случае отсутствия перекрытия до вставки, проходим через массив интервалов до тех пор, пока конечная точка текущего интервала меньше начальной точки нового интервала. Добавляем текущий интервал в массив res и переходим к следующему.
В случае перекрытия, продолжаем обход, пока начальная точка нового интервала меньше или равна конечной точке текущего интервала. Обновляем начальные и конечные точки нового интервала, объединяя перекрывающиеся интервалы в один.
Проходим через оставшиеся интервалы после индекса i и добавляем их в массив res. Это включает интервалы, которые следуют после нового интервала и не перекрываются с ним.
Возвращаем массив res, содержащий все интервалы с корректно вставленным новым интервалом.
class Solution {
public:
vector<vector<int>> insert(vector<vector<int>>& intervals, vector<int>& newInterval) {
int n = intervals.size(), i = 0;
vector<vector<int>> res;
while (i < n && intervals[i][1] < newInterval[0]) {
res.push_back(intervals[i]);
i++;
}
while (i < n && newInterval[1] >= intervals[i][0]) {
newInterval[0] = min(newInterval[0], intervals[i][0]);
newInterval[1] = max(newInterval[1], intervals[i][1]);
i++;
}
res.push_back(newInterval);
while (i < n) {
res.push_back(intervals[i]);
i++;
}
return res;
}
};Please open Telegram to view this post
VIEW IN TELEGRAM
❤2