| tags |
|
|
|---|---|---|
| e_maxx_link | duval_algorithm |
First let us define the notion of the Lyndon factorization.
A string is called simple (or a Lyndon word), if it is strictly smaller than any of its own nontrivial suffixes.
Examples of simple strings are:
Next, let there be a given string
It can be shown, that for any string such a factorization exists and that it is unique.
The Duval algorithm constructs the Lyndon factorization in
First let us introduce another notion:
a string
The Duval algorithm is greedy.
At any point during its execution, the string
Let's describe the algorithm in more detail.
The pointer
-
$s[j] = s[k]$ : if this is the case, then adding the symbol$s[j]$ to$s_2$ doesn't violate its pre-simplicity. So we simply increment the pointers$j$ and$k$ . -
$s[j] > s[k]$ : here, the string$s_2 + s[j]$ becomes simple. We can increment$j$ and reset$k$ back to the beginning of$s_2$ , so that the next character can be compared with the beginning of the simple word. -
$s[j] < s[k]$ : the string$s_2 + s[j]$ is no longer pre-simple. Therefore we will split the pre-simple string$s_2$ into its simple strings and the remainder, possibly empty. The simple string will have the length$j - k$ . In the next iteration we start again with the remaining$s_2$ .
Here we present the implementation of the Duval algorithm, which will return the desired Lyndon factorization of a given string
vector<string> duval(string const& s) {
int n = s.size();
int i = 0;
vector<string> factorization;
while (i < n) {
int j = i + 1, k = i;
while (j < n && s[k] <= s[j]) {
if (s[k] < s[j])
k = i;
else
k++;
j++;
}
while (i <= k) {
factorization.push_back(s.substr(i, j - k));
i += j - k;
}
}
return factorization;
}Let us estimate the running time of this algorithm.
The outer while loop does not exceed
So we are only interested in the first inner while loop.
How many iterations does it perform in the worst case?
It's easy to see that the simple words that we identify in each iteration of the outer loop are longer than the remainder that we additionally compared.
Therefore also the sum of the remainders will be smaller than
Let there be a string
The beginning of the simple block can be found easily - just remember the pointer
So we get the following implementation:
string min_cyclic_string(string s) {
s += s;
int n = s.size();
int i = 0, ans = 0;
while (i < n / 2) {
ans = i;
int j = i + 1, k = i;
while (j < n && s[k] <= s[j]) {
if (s[k] < s[j])
k = i;
else
k++;
j++;
}
while (i <= k)
i += j - k;
}
return s.substr(ans, n / 2);
}