Если у меня есть строка, представляющая целое число в двоичной форме, например

1101101

И я хочу по кругу сдвинуть его вправо, чтобы получить

1110110

Один из способов, который я мог придумать, - это преобразовать строку в int и использовать (взято из википедия)

// https://stackoverflow.com/a/776550/3770260
template <typename INT>
#if __cplusplus > 201100L // Apply constexpr to C++ 11 to ease optimization
constexpr
#endif // See also https://stackoverflow.com/a/7269693/3770260
INT rol(INT val, size_t len) {
#if __cplusplus > 201100L && _wp_force_unsigned_rotate // Apply unsigned check C++ 11 to make sense
    static_assert(std::is_unsigned<INT>::value,
                  "Rotate Left only makes sense for unsigned types");
#endif
    return (val << len) | ((unsigned) val >> (-len & (sizeof(INT) * CHAR_BIT - 1)));
}

Однако, если строка состоит, скажем, из 10 ^ 6 char, то это не работает, поскольку целочисленное представление превышает даже диапазон __int64.

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

//let str be a char string of length n
char temp = str[n - 1];
for(int i = n - 1; i > 0; i--)
{
    str[i] = str[i - 1];
}
str[0] = temp;

Это решение выполняется в O(n) из-за цикла по длине строки n. Мой вопрос: есть ли более эффективный способ реализовать циклический сдвиг для больших двоичных строк?

РЕДАКТИРОВАТЬ

И вход, и выход - std::string s

1
iambrj 10 Ноя 2018 в 03:47

1 ответ

Лучший ответ

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

Вы также можете использовать стандартные функции std::string:

str.insert(str.begin(), str[n - 1]);
str.erase(str.end() - 1);

Или memmove, или memcpy (я на самом деле не рекомендую это, это для аргумента)

char temp = str[n - 1];
memmove(str.data() + 1, str.data(), n - 1);
str[0] = temp;

Обратите внимание, что memmove может выглядеть быстрее, но по сути это то же самое, что и ваш цикл. Он перемещает байты один за другим, он просто инкапсулируется в другую функцию. Этот метод может быть быстрее для блоков данных гораздо большего размера, размером 1000 байт и более, поскольку ЦП оптимизирован для перемещения больших блоков памяти. Но вы не сможете измерить разницу для 10 или 20 байт.

Более того, компилятор, скорее всего, выполнит дополнительную оптимизацию, когда увидит ваш цикл for, поймет, что вы перемещаете память, и выберет лучший вариант.

Компилятор также хорошо справляется с методами std::string. Это обычные операции, и компилятор знает, как с ними справиться.

2
Barmak Shemirani 10 Ноя 2018 в 07:41