Пишем функцию RLE для компрессии строки
В этом уроке мы рассмотрим решение задачи по компрессии строки с помощью кодирования повторов. Такое кодирование называется RLE (Run-length encoding). Сутью алгоритма является замена повторяющихся символов цифрой, обозначающей количество повторов. Кодирование чаще всего применяется для данных, которые содержат большое количество серий одинаковых символов, например, иконки и графические рисунки.
Умение решать эту задачу может пригодиться разработчику как на собеседовании, так и во время работы со строками на реальных проектах.
Условия задачи
Дана строка, состоящая из букв A-Z: 'AAAABBBCCXYZDDDDEEEFFFAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBB'
Нужно написать функцию, которая:
- на выходе вернет строку вида
'A4B3C2XYZD4E3F3A6B28'
- сгенерирует любую ошибку, если на вход пришла невалидная строка
Дополнительные пояснения:
- eсли символ встречается 1 раз, он остается без изменений
- если символ повторяется более 1 раза, к нему добавляется количество повторений
function rle(str) {
// your code
}
Подготовка
Перед тем, как приступить к задаче, нужно убедится в том, что условия прозрачны и дополнительных вопросов и узких кейсов нет. Добавим несколько кейсов, которые помогут валидировать решение.
console.log(rle('AAAABBBCCXYZDDDDEEEFFFAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBB')) // 'A4B3C2XYZD4E3F3A6B28'
console.log(rle('ABC')) // 'ABC'
console.log(rle('')) // ''
console.log(rle('A4B3C2XYZD4E3F3A6B28')) // Error
На этом этапе также стоит обдумать пути решения задачи и предположить ожидаемую
алгоритмическую сложность. Мы будем писать решение с линейной алгоритмической
сложностью O(n)
, то есть скорость выполнения будет напрямую зависеть
от длины строки.
Решим задачу путем последовательного перебора символов и использования указателей для хранения текущего состояния перебора. Понадобится 3 указателя для хранения промежуточного результата, текущей буквы и количества повторов.
Решение
В первую очередь на раннем этапе обработаем редкие кейсы, когда будем возвращать ошибку или пустую строку.
function rle(str) {
if (str === "") return str;
if (!str.match(/^[A-Z]+$/)) {
throw new Error(`'${str}' is invalid input for rle()`);
}
// ...
}
Объявим переменные-указатели для хранения промежуточного состояния с начальными значениями.
Для получения символа можно использовать метод charAt
. Он повышает читаемость кода,
указывая на то, что в переменной строка. Этот метод работает даже в IE7, в отличие
от обращения по индексу. Однако, обращение по индексу str[index]
тоже ипользуется
повсеместно.
function rle(str) {
// ...
let result = "",
current = str.charAt(0),
count = 1;
// ...
}
Линейно итерируем по строке, начиная со второго символа. Первый у нас уже выбран как текущий. Сравниваем текущий символ с символом из итерации. Если они разные, записываем текущий символ в финальную строку. При наличии более одного повтора добавляем к символу количество повторов. Если символы совпадают, итерируем дальше и увеличиваем счетчик повторов.
function rle(str) {
// ...
for (let i = 1; i < str.length; i++) {
if (str.charAt(i) !== current) {
result += current;
if (count > 1) {
result += count;
count = 1;
}
current = str.charAt(i);
} else {
count++;
}
}
// ...
}
После выполнения цикла текущая буква в строку не попадает, поэтому перед возвратом финальной строки нужно её добавить. В результате функция выглядит следующим образом:
function rle(str) {
if (str === "") return str;
if (!str.match(/^[A-Z]+$/)) {
throw new Error(`'${str}' is invalid input for rle()`);
}
let result = "",
current = str.charAt(0),
count = 1;
for (let i = 1; i < str.length; i++) {
if (str.charAt(i) !== current) {
result += current;
if (count > 1) {
result += count;
count = 1;
}
current = str.charAt(i);
} else {
count++;
}
}
result += current;
return result;
}
Помимо оценки сложности алгоритма по скорости выполнения, оценивают и сложность по
используемой памяти. При увеличении длины входной строки количество используемых
переменных не увеличивается, поэтому временная сложность по памяти этой
функции - O(1)
, то есть константная.
Проработка
Решите задачу, пройдя все подготовленные тесты.
Вернуться на главную