Here's the problem I have a hard time solving.
You are given a ciphertext Y and a sequence of cyclic shifts that had produced Y from string Z, the shift with parameters (i, j, k) applies to the substring Z[i..j] (from i-th to j-th character, inclusive) and cyclicly rotates it to the right k times. String characters are numbered starting from one. Given the above information, your task is to guess the initial cleartext X.
Input:
The first line contains the ciphertext, which is a nonempty string consisting of N lowercase English letters (1 ≤ N ≤ 50000). The second line contains the number of shifts M (1 ≤ M ≤ 50000). The following M lines describe the sequence of cyclic shifts (in the order of their application to the cleartext). Each shift is described by three parameters i, j, k (1 ≤ i < j ≤ N, 1 ≤ k ≤ j − i).
Example of input:
logoduck
3
1 3 1
4 5 1
1 4 1
As output you should provide the deciphered text (``goodluck'' for example).
The obvious approach is to try and reverse each shift starting from the last one. It seems that this approach is not time-efficient. However, I can't come up with any ideas how to do it any other way, so any help is appreciated.
I attach my code:
#include <iostream>
#include <vector>
#include <string>
int main() {
std::string message;
std::cin >> message;
int number_of_elements = message.size();
int elements[number_of_elements];
for (int i = 0; i < number_of_elements; ++i) {
elements[i] = i;
}
int number_of_shifts;
std::cin >> number_of_shifts;
std::vector<std::vector<int>> shifts(number_of_shifts);
for (int iterator = 0; iterator < number_of_shifts; ++iterator) {
int left, right, by;
std::cin >> left >> right >> by;
--left;
--right;
shifts[iterator].push_back(left);
shifts[iterator].push_back(right);
shifts[iterator].push_back(by);
}
for (int iterator = number_of_shifts - 1; -1 < iterator; --iterator) {
int current[number_of_elements];
int left, right, by;
left = shifts[iterator][0];
right = shifts[iterator][1];
by = shifts[iterator][2];
for (int j = right; left - 1 < j; --j) {
if (j - by < left) {
current[right + 1 - (left - (j - by))] = elements[j];
} else {
current[j - by] = elements[j];
}
}
for (int j = left; j < right + 1; ++j) {
elements[j] = current[j];
}
}
for (int i = 0; i < number_of_elements; ++i) {
std::cout << message.substr(elements[i], 1);
}
return 0;
}
You can do this in O(M log N) time using a data structure called a "rope", which is like a string that supports splitting and concatenation in O(log N) time: https://en.wikipedia.org/wiki/Rope_(data_structure)
Rotations can be build from these operations, of course.
The implementation is a binary tree, B-tree, or similar with limited-size strings in the leaves.
It's not hard to find C++ implementations, but unfortunately, the STL doesn't have one. If you have to implement it yourself, it's a little tricky.