Редакция для Двойной двоичный код в строку
Submitting an official solution before solving the problem yourself is a bannable offence.
1. Идея
Строку закодировали два раза одним и тем же способом:
- каждый символ исходной строки заменили его 8-битным ASCII-кодом;
- затем каждый символ получившейся двоичной строки, то есть символ
'0'или'1', снова заменили его 8-битным ASCII-кодом.
Нужно выполнить обратную операцию тоже дважды.
Ключевая мысль: на втором этапе кодировались только символы '0' и '1'. Их ASCII-коды известны:
'0'→00110000'1'→00110001
Значит, если разбить вход на блоки по 8 бит, каждый блок можно превратить обратно в символ '0' или '1'. Так мы восстановим промежуточную двоичную строку.
После этого остаётся ещё раз декодировать её: снова разбить на блоки по 8 бит, и каждый такой блок перевести в ASCII-символ исходного сообщения.
2. Наблюдения
Наблюдение 1
Так как на втором этапе каждый символ промежуточной строки заменяется 8 битами, итоговая строка состоит из блоков длины 8.
Причём каждый блок — это либо:
00110000, что означает символ'0',00110001, что означает символ'1'.
Наблюдение 2
После первого обратного шага получится строка, состоящая только из символов '0' и '1'. Это и есть результат первого кодирования.
Её длина кратна 8, потому что на первом этапе каждый символ исходной строки тоже заменялся 8 битами.
Наблюдение 3
Чтобы восстановить символ по 8-битной двоичной записи, можно просто посчитать её численное значение в двоичной системе:
- идём слева направо;
- текущий
valueумножаем на 2; - прибавляем текущий бит
0или1.
После этого value — это ASCII-код символа.
3. Алгоритм
- Считать строку
s. - Восстановить промежуточную строку
first:- идти по
sблоками по 8 символов; - если блок равен
00110000, добавить вfirstсимвол'0'; - если блок равен
00110001, добавить вfirstсимвол'1'.
- идти по
- Восстановить ответ:
- идти по строке
firstблоками по 8 символов; - для каждого блока посчитать его значение как двоичное число;
- преобразовать число в символ ASCII и добавить в ответ.
- идти по строке
- Вывести ответ.
4. Почему это работает
На втором этапе кодирования каждый символ строки из '0' и '1' был заменён на свой ASCII-код длины 8.
ASCII-коды символов '0' и '1' однозначны:
'0'это00110000,'1'это00110001.
Поэтому, если разбить итоговую строку на 8-битные куски, каждый кусок однозначно восстанавливает один символ промежуточной строки. После обработки всех блоков мы точно получаем строку, которая была после первого этапа кодирования.
Далее эта промежуточная строка была получена из исходного текста стандартным способом: каждый символ заменили его 8-битным ASCII-кодом. Значит, если снова разбить её на блоки по 8 бит и перевести каждый блок из двоичной записи в число, а число — в символ ASCII, мы восстановим исходное сообщение.
Таким образом, алгоритм выполняет точное обратное преобразование к двум этапам кодирования, значит ответ будет правильным.
5. Сложность
Пусть длина входной строки равна n.
- Первый проход по строке обрабатывает все символы один раз.
- Второй проход тоже обрабатывает все символы один раз.
Итоговая сложность: O(n).
Дополнительная память: O(n), так как хранится промежуточная строка.
6. Код на C++17
#include <iostream>
#include <string>
#include <cstdio>
#if defined(_WIN32)
#include <io.h>
#include <fcntl.h>
#endif
using namespace std;
int main() {
#if defined(_WIN32)
// Ensure raw bytes are written without CRLF translation.
_setmode(_fileno(stdout), _O_BINARY);
#endif
string s;
cin >> s;
string first;
for (int i = 0; i < (int)s.size(); i += 8) {
string block = s.substr(i, 8);
if (block == "00110000") {
first += '0';
} else if (block == "00110001") {
first += '1';
}
}
string answer;
for (int i = 0; i < (int)first.size(); i += 8) {
int value = 0;
for (int j = 0; j < 8; ++j) {
value = value * 2 + (first[i + j] - '0');
}
answer += char(value);
}
fwrite(answer.data(), 1, answer.size(), stdout);
fflush(stdout);
return 0;
}
Важно: ответ — это произвольные байты (значения 0..255), среди которых могут
встречаться \n (0x0A) и непечатаемые символы. Поэтому вывод выполняется
именно как «сырые» байты (fwrite + бинарный режим stdout на Windows), а не
через текстовый поток, который мог бы преобразовать перевод строки в \r\n.
7. Код на Python 3
import sys
s = input().strip()
first = []
for i in range(0, len(s), 8):
block = s[i:i + 8]
if block == "00110000":
first.append('0')
elif block == "00110001":
first.append('1')
first = ''.join(first)
answer = []
for i in range(0, len(first), 8):
block = first[i:i + 8]
value = 0
for ch in block:
value = value * 2 + (ord(ch) - ord('0'))
answer.append(value)
sys.stdout.buffer.write(bytes(answer))
Здесь ответ собирается как список целых 0..255 и пишется в sys.stdout.buffer
как «сырые» байты, чтобы значения >= 128 не искажались текстовой кодировкой
консоли (иначе, например, байт 0xFF вышел бы как два байта в UTF-8).
Комментарии