Editorial for Google Code Jam '22 Round 1C Problem A - Letter Blocks
Submitting an official solution before solving the problem yourself is a bannable offence.
Let us first notice that this problem is equivalent to finding an order in which partial strings should be concatenated such that occurrences of the same letter appear together.
Test Set 1
Since is at most , the number of permutations of these strings is at most .
We can therefore generate all permutations and for each of them, we need to verify the final string which results of concatenating the input strings in that particular order.
Let us take a look at the string CCCABDAEEF
. To verify the string it is enough to:
- Get a set of letters: {A, B, C, D, E, F}
- Create a grouped representation of a string:
CABDAEF
- The string is good if and only if the lengths coincide.
If for at least one permutation the size matches, we can print out this string. Otherwise, it's impossible.
The time complexity of this solution is , which means times the sum of the lengths of the input strings, because there are permutations and the verification of the final string takes linear time.
Test Set 2
In this test set can be , so is too large to enumerate all permutations.
First of all, we can verify that each of the strings meets the requirements from the task. This can be done by applying the verification method described in the section above for each individually. If the verification fails for one of the input strings, then it will certainly fail for any permutation of them and therefore we output IMPOSSIBLE
.
Let us call middle letters all letters other than the first and last consecutive segment of letters. Next, let us notice that if the string has more than distinct letters, then:
- If a letter is a middle letter in more than one input string, then those occurrences will not be together in the final string regardless of the order in which we concatenate them. Therefore, this case is impossible.
- If the middle letters exist in a single input string, they don't influence the outcome as those occurrences will be together in the final string regardless of the order in which we concatenate. Therefore, in this case we can assume the input string is simply two letters long removing everything except the first and last letter of it.
Therefore, for each middle letter we can count in how many strings it appears and if the answer is more than for any of them, we can print out IMPOSSIBLE
.
Since we also verified each string already, we know that each letter appears only in block inside each .
After this step the problem is now simplified into strings of two forms:
- : Represents the string consisting of only one block of letter .
- : Represents the string starting with a block of letters and ending with a block of letters.
If there are two strings of the form for the same letter, we can concatenate them as they can't be separated by other strings in the final solution. If there are two strings of the form and , then the answer is IMPOSSIBLE
if (due to ) or (due to ), because it means that in any ordering, there would be at least one block of a different letter between letters and .
Therefore for each string , we can create the following mappings using sets:
- If is of the form , then insert into .
- If is of the form , then insert into both and .
In other words, , and must all contain exactly element for each letter . If the element already exists, we return IMPOSSIBLE
.
Starting string
Let us consider starting string as the input string which is not forced by any previous strings in the final answer. When can the given string be a starting string?
- If is of the form , then there must be no other strings ending with letter , i.e. .
- If is of the form , then and and .
With these two conditions, we consider a set of candidates containing all such starting strings.
Extending the block
Let us consider we already built the partial answer which ends with letter . If there exists a string at , it is the last chance to append it, because otherwise it would be separated by at least one block of another letter. Similarly, if there exists a string at , we must extend it now for the same reason.
How to choose the starting string?
It turns out that for starting a new block, we can choose an arbitrary candidate from the candidates set.
Proof: Let us assume that we picked string as the starting string but the optimal solution started the block with . Let us consider the swapped optimal solution in which we swap these blocks.
Let start with letter and with letter . Then:
- Optimal = |b..X|a....Y|
- Optimal (swapped) = |a....Y|b..X|
Let us consider what happens after swapping:
- Middle letters of these blocks: Any letters between and can't be after and any letters after can't be before . Therefore after swap they also remain fine.
- Letters : Optimal solution can't have any letters after . Therefore this swap is okay.
- Letters : Since belongs to the candidate set, therefore or . It means, that there are either no strings ending in or is the only string ending with . Therefore can't end with and the swap remains correct.
Final solution
Repeat:
- Pick an arbitrary string from the candidate's set and start the block with it.
- Let e = last letter of the current solution. If starts[e] != null, add starts[e] to current solution and repeat step 2. Otherwise goto step 1.
- If candidate's set is empty, print solution. Otherwise, print
IMPOSSIBLE
.
Time complexity: , since we are touching each candidate only once.
Comments