I have a string S
which consists of a
's and b
's. Perform the below operation once. Objective is to obtain the lexicographically smallest string.
Operation: Reverse exactly one substring of S
e.g.
S = abab
then Output = aabb
(reverse ba
of string S
)S = abba
then Output = aabb
(reverse bba
of string S
)My approach
Case 1: If all characters of the input string are same then output will be the string itself.
Case 2: if S
is of the form aaaaaaa....bbbbbb....
then answer will be S
itself.
otherwise: Find the first occurence of b
in S
say the position is i. String S
will look like
aa...bbb...aaaa...bbbb....aaaa....bbbb....aaaaa...
|
i
In order to obtain the lexicographically smallest string the substring that will be reversed starts from index i. See below for possible ending j.
aa...bbb...aaaa...bbbb....aaaa....bbbb....aaaaa...
| | | |
i j j j
Reverse substring S[i:j]
for every j and find the smallest string.
The complexity of the algorithm will be O(|S|*|S|)
where |S|
is the length of the string.
Is there a better way to solve this problem? Probably O(|S|)
solution.
What I am thinking if we can pick the correct j
in linear time then we are done. We will pick that j where number of a
's is maximum. If there is one maximum then we solved the problem but what if it's not the case? I have tried a lot. Please help.
Given a string S and an integer K, the task is to find the lexicographically smallest string possible after reversing any substring of any length exactly K times. After 3rd operation: S = “abcdgfzfge”, in S select S[3 – 6] = “zfgd” and reverse it.
Suppose we have two arrays A and B with n numbers, we have to rearrange the elements of B in itself in a way such that the sequence formed by (A[i] + B[i]) % n after rearranging it is lexicographically smallest.
The lexicographically smallest ARR is obtained if the smallest element is present at the beginning i.e ARR[0], followed by the next smallest at index 1 (ARR[1]) and so on. The number of swaps to move an element from index 'i' to index 'j' (where i > j) is i - j as we can swap only neighbouring elements.
So, I came up with an algorithm, that seems to be more efficient that O(|S|^2), but I'm not quite sure of it's complexity. Here's a rough outline:
a's
, storing in variable start
.a's
.index
remains, proceed to 10.b's
after reversal is at a minimum.index
remains, proceed to 10.a's
(not including the leading a's
) after reversal is at a minimum.index
remains, proceed to 10.a's
and b's
this time.start
, plus the reversed groups up to index
, plus the remaining groups.Since any substring that is being reversed begins with a b
and ends in an a
, no two hypothesized reversals are palindromes and thus two reversals will not result in the same output, guaranteeing that there is a unique optimal solution and that the algorithm will terminate.
My intuition says this approach of probably O(log(|S|)*|S|), but I'm not too sure. An example implementation (not a very good one albeit) in Python is provided below.
from itertools import groupby
def get_next_bs(i, groups, off):
d = 1 + 2*off
before_bs = len(groups[i-d]) if i >= d else 0
after_bs = len(groups[i+d]) if i <= d and len(groups) > i + d else 0
return before_bs + after_bs
def get_next_as(i, groups, off):
d = 2*(off + 1)
return len(groups[d+1]) if i < d else len(groups[i-d])
def maximal_reversal(s):
# example input: 'aabaababbaababbaabbbaa'
first_b = s.find('b')
start, rest = s[:first_b], s[first_b:]
# 'aa', 'baababbaababbaabbbaa'
groups = [''.join(g) for _, g in groupby(rest)]
# ['b', 'aa', 'b', 'a', 'bb', 'aa', 'b', 'a', 'bb', 'aa', 'bbb', 'aa']
try:
max_length = max(len(g) for g in groups if g[0] == 'a')
except ValueError:
return s # no a's after the start, no reversal needed
indices = [i for i, g in enumerate(groups) if g[0] == 'a' and len(g) == max_length]
# [1, 5, 9, 11]
off = 0
while len(indices) > 1:
min_bs = min(get_next_bs(i, groups, off) for i in indices)
indices = [i for i in indices if get_next_bs(i, groups, off) == min_bs]
# off 0: [1, 5, 9], off 1: [5, 9], off 2: [9]
if len(indices) == 1:
break
max_as = max(get_next_as(i, groups, off) for i in indices)
indices = [i for i in indices if get_next_as(i, groups, off) == max_as]
# off 0: [1, 5, 9], off 1: [5, 9]
off += 1
i = indices[0]
groups[:i+1] = groups[:i+1][::-1]
return start + ''.join(groups)
# 'aaaabbabaabbabaabbbbaa'
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With