Leetcode Challenge
22 subscribers
1 photo
42 links
Пытаюсь решать задачки с leetcode
加入频道
[Условие] Leetcode #10. Regular Expression Matching

Ссылка на задачу на Leetcode
Решение

Нужно реализовать простейшее регулярное выражение. На вход получаем строку s и паттерн p. Задача - проверить, соответствует ли строка паттерну.
Проверяется полное совпадение строки - от начала и до конца.

Строка содержит только английские буквы в нижнем регистре.

Паттерн может содержать английские буквы в нижнем регистре, а также символы . и *.
При этом символ . означает "один любой символ".
А символ * относится к предыдущему символу и означает "0 или больше". То есть паттерн "a*" соответствует строкам "a", "aa", "aaaaaa", а также пустой строке "".

В паттерне может быть комбинация ".*", которая означает "0 и более любых символов".


Пример #1:

s = "aa", p = "a"
output: false (строка совпадает не полностью)

Пример #2:

s = "aa", p = "a*"
output: true

Пример #3:

s = "ab", p = ".*"
output: true


Подсказка: следует использовать динамическое программирование

#hard #dp

@leetcode_furrycat
[Условие] Leetcode #4. Median of Two Sorted Arrays

Ссылка на задачу на Leetcode
Решение (часть 1 и часть 2)

Получаем два отсортированных массива: nums1 с длиной m и nums2 с длиной n. Возвращаем их медиану (серединное значение). Временная сложность должна быть O(log (m + n)).


Кейс #1:
nums1 = [1,3]
nums2 = [2]
Output: 2
Объяснение: общий массив - [1,2,3], медиана - 2

Кейс #2:
nums1 = [1,2]
nums2 = [3,4]
Output: 2.5
Объяснение: общий массив - [1,2,3,4], медиана - (2+3)/2 = 2.5


#hard #arrays

@leetcode_furrycat
[Решение] Leetcode #4. Median of Two Sorted Arrays. Решение (Часть 1)

Условие задачи

Решение в лоб: слить отсортированные массивы в один. Но оно не соответствует заявленной сложности (будет O(n)), поэтому ищем другие пути.

Левая половина

Нам достаточно получить левую половину результирующего массива (до медианы).


если смерженный массив: [1,2,3,4,5]
то его левая половина: [1,2]

если смерженный массив: [1,2,3,4]
то его левая половина: [1,2]


У нас есть два массива, arr1 и arr2. Пусть arr1.length меньше или равна arr2.length, то есть arr1 короче. Будем называть смерженный массив mergedArr, а его левую половину mergedArrLeft.

Легко узнать, сколько элементов входят в mergedArrLeft. Находим общую длину двух массивов (`total`) и делим ее на 2 (`half`). Таким образом в mergedArrLeft входит half элементов.


если mergedArr: [1,2,3,4,5]
total = 5
half = Math.floor(total / 2) = 2
mergedArrLeft: [1,2]

mergedArr: [1,2,3,4]
total = 4
half = Math.floor(total / 2) = 2
mergedArrLeft: [1,2]


Наша задача быстро составить mergedArrLeft. Зная, чем заканчивается левая половина, мы сможем найти медиану.

Сделаем предположение, что в mergedArrLeft входит половина массива arr1 (более короткого массива). Почему половина? Ну, нужно же с чего-то начинать, а начинаем мы обычно с принципа "разделяй и властвуй".

Внимание: нет гарантии, что это предположение окажется верным. Мы будем его корректировать по мере необходимости.

Итак, берем половину arr1, считаем, сколько это элементов. Вычитаем это число из half, чтобы понять, сколько элементов нужно взять из второго массива, чтобы составить mergedArrayLeft.


arr1 = [1,2,3,4,5]
arr2 = [1,2,3,4,5,6,7,8]

total = 5 + 8 = 13
half = Math.floor(13 / 2) = 6

arr1MedianIndex = Math.floor(5 / 2) = 2
arr1Elements = [1,2,3] // с 0го по 2й элемент включительно

arr2ElementsCount = 3 // 6 - 3
arr2Elements = [1,2,3]


Проверка

Теперь нам нужно убедиться, что мы предположили правильно, и действительно именно эти элементы составляют mergedArrLeft. Это очень просто. Нужно проверить, что последний элемент каждого массива меньше или равен первому элементу, оставшемуся в другом массиве.

То есть мы берем из arr1 элементы [1,2,3]. В нем остаются [4,5]. А из arr2 мы берем элементы [1,2,3].


arr1 = [1,2,3,4,5]
arr1Elements = [1,2,3] // входит в arrMergedLeft
arr1Rest = [4,5]

arr2 = [1,2,3,4,5,6,7,8]
arr2Elements = [1,2,3] // входит в arrMergedLeft
arr2Rest = [4,5,6,7,8]


Последний элемент из arr1Elements не может быть больше первого элемента из arr2Rest и наоборот.

Если все верно, то мы легко можем найти последний элемент mergedArrLeft (самое большое значение из arr1Elements и arr2Elements`) и следующий за ним элемент (самое маленькое значение из `arr1Rest и arr2Rest`). Если `total - четное, нам нужно будет найти их среднее арифметическое, если нечетное, то следующий элемент и является медианой.

На данный момент сложность алгоритма константная - O(1), так как мы выполняем только арифметические операции.

#hard #arrays

@leetcode_furrycat
Leetcode #4. Median of Two Sorted Arrays. Решение (Часть 2)

Корректировка

Если проверка не прошла - мы неправильно определили состав mergedArrLeft. Значит, нужно сдвинуть arr1Elements. Куда именно, зависит от того, какая конкретно проверка не прошла. Если arr1Elements оказался больше, чем надо, то его нужно уменьшить. И соответственно, наоборот.

Посмотрим на примере:


arr1 = [1,2,3,4]
arr2 = [1,2,3,4,5,6,7,8]

total = 4 + 8 = 12
half = Math.floor(12 / 2) = 6

arr1MedianIndex = Math.floor(4 / 2) = 2
arr1Elements = [1,2]
arr1Rest = [3,4]

arr2ElementsCount = 3 // 6 - 2
arr2Elements = [1,2,3,4]
arr2Rest = [5,6,7,8]


Тут проверка не проходит, так как последний элемент arr2Elements больше, чем первый элемент arr1Rest. Получается, что мы взяли слишком мало элементов из arr1, нужно больше.

Те элементы, что уже взяты ([1,2]) мы отбросим, так как они точно входят в arrMergedLeft. Возьмем оставшиеся - и повторим с ними все то же самое: разделим пополам, и предположим, что первая половина входит в arrMergedLeft.

Таким образом, arr1Elements увеличится, придется все пересчитать.


arr1Elements = [1,2,3]
arr1Rest = [4]

arr2ElementsCount = 3
arr2Elements = [1,2,3]
arr2Rest = [4,5,6,7,8]


Теперь проверка проходит.

Код

В разборе для наглядности использовалось количество элементов в частях массивов. Разумеется, в коде удобнее и проще использовать индексы. То есть мы не будем вычислять arr2ElementsCount, а вычислим, элементы до какого индекса входят в arrayMergedLeft.

Также мы будем использовать указатели left и right для обозначения той части массива arr1, с которой мы сейчас работаем. Изначально они будут стоять по краям массива (`0` и `arr1.length - 1`). Если проверка не пройдет, мы будем их сдвигать (либо один, либо другой).

Сложность

В неудачном случае (если проверка не проходит), мы делим массив arr1 надвое - "разделяй и властвуй", следовательно сложность алгоритма составляет O(log(n)).

Решение (видео, англ.)

#hard #arrays

@leetcode_furrycat