Александр Нестеров привёл решение на псевдокоде, но вопрос под тегом Питон... Дело в том, что питон не поощряет изменение входных строк в решениях такого рода.
Поэтому приведу своё решение перфекциониста=) Если кто укажет как его улучшить, буду рад=)
Возможно новичку применённые там трюки будут сложны в понимании или даже местами вредны, но точно познавательны.
import unittest
import typing
_VARIANTS = [] # Сюда попадут все решения, которые нужно тестировать
# А это декоратор, которым нужно пометить все претенденты на решение
solution = lambda f: _VARIANTS.append(f) or f
EXAMPLES = [
('', ''),
('a', 'a1'),
('abc', 'a1b1c1'),
('aaaabbca', 'a4b2c1a1'),
]
@solution
def encode(inp: str) -> str:
"""Самое простое решение
Минусы: громоздко и многословно
(зато прозрачно и понятно, без магии и выкрутасов)
"""
if not inp:
return ''
count = 1
current = inp[0]
res = []
for c in inp[1:]:
if c == current:
count += 1
else:
res.append(f'{current}{count}')
count = 1
current = c
res.append(f'{current}{count}')
return ''.join(res)
def encode_stream(inp: typing.TextIO) -> typing.Iterator:
"""Потоковое решение.
С помощью него можно без затрат памяти кодировать файлы любого размера
Минусы: нет, если вам нужно закодировать гигабайты.
Ну или готов поспорить на эту тему, если вы не согласны.=)
"""
current = inp.read(1)
count = len(current)
while current:
c = inp.read(1)
if c == current:
count += 1
else:
yield f'{current}{count}'
current = c
count = len(current)
@solution
def encode_string_by_stream(inp: str) -> str:
"""Обёртка для использования потокового кодирования из строки"""
import io
return ''.join(encode_stream(io.StringIO(inp)))
@solution
def encode_elegant(s: str) -> str:
"""Довольно элегантное решение на словаре от @seven5674.
К сожалению в оригинальном варианте неверное, но я
исправил и отрефакторил.
Минусы: запутанное и непрозрчное, зато короткое"""
d = {}
g = 1
for c in s:
g = d.get((c, g), 0) and g or g + 1
d[c, g] = d.get((c, g), 0) + 1
return ''.join([f'{k[0]}{v}' for k, v in d.items()])
@solution
def encode_by_regexp(s: str) -> str:
"""Решение на регекспах от @seven5674,
я лишь чуть отформатировал и отрефакторил.
Минусы: регексп поведёт себя довольно непредсказуемо на больших данных,
к тому же регекспы читать умеют не все.
Но автор умеет в регекспы лучше, чем в питон, видимо с js пришел.
"""
import re
return ''.join(
f'{w}{len(l)}'
for l, w in
re.findall(r"((\w)+?(?!\2))", s)
)
#############################################################################
## Дальше идёт инфраструктура для тестирования решений
class Test(unittest.TestCase):
"""Автоматический тест решений.
Претенденты на решение должны быть помечены декоратором @solution
Примеры берутся из списка EXAMPLES.
"""
def closure(func, arg, res):
"""Временная функция, которая делает тест.
Она формирует каждый раз новую функуию-замыкание, которая будет тестировать
оережной кейс.
"""
def test(self):
f"""Тест функции {func.__name__}({arg!r})"""
self.assertEqual(func(arg), res, msg=f'Func {func.__name__}({arg!r})')
return test
# Перебираем все варианты реализаций:
for f in _VARIANTS:
# Перебираем все предлженные эталонные римеры:
for case_num, case in enumerate(EXAMPLES):
# Создаём новую функуию теста и добавляем ее в класс теста как метод.
locals()[f'test_{f.__name__}__{case_num}'] = closure(f, *case)
# Удаляем из контекста класса лишние переменные
del(closure, f, case_num, case)
if __name__ == '__main__':
unittest.main(verbosity=3)
UPD: Обновил код, включив туда решения
seven5674 и
valerr007
Кстати, решение
valerr007 проваливает тест с "a" на входе. Возвращает пустую строку. Ну и много претензий к коду.