CF 102780C - Emoticons
The input is a scrambled collection of characters that originally came from writing several emoticons one after another. The order of characters was lost, but the total number of occurrences of every character was preserved.
Rating: -
Tags: -
Solve time: 1m 1s
Verified: yes
Solution
Problem Understanding
The input is a scrambled collection of characters that originally came from writing several emoticons one after another. The order of characters was lost, but the total number of occurrences of every character was preserved. The task is to recover any possible list of emoticons that could have produced exactly this multiset of characters, then print each recovered emoticon and finish with LOL.
The important detail is that the order of characters does not matter. We are not searching for substrings inside the input. We are solving a reconstruction problem where the only information available is the frequency of each character.
The input length can reach 10000 characters. A solution that tries every possible combination of emoticons would quickly become impossible because the number of combinations grows exponentially with the number of emoticons. Since there are only 16 possible emoticon types, the intended solution must exploit their character structure rather than the input length. Counting characters and doing constant-size arithmetic is enough, because the expensive part of the problem is not the string length but finding a valid decomposition.
Several edge cases can break a naive implementation. A common mistake is assuming that emoticons can be greedily removed by appearance. For example, the input
:-0
contains one zero, one dash, one colon. It could only represent :-0, not :- plus something else, because there is no emoticon consisting only of those remaining pieces.
Another tricky case is the interaction between the two emoticons containing parentheses. Consider:
;:((
A careless approach might decide that ;-) or ;-( must appear because of the semicolon. The correct interpretation is:
;-(
:(
The semicolon only tells us that one of the semicolon emoticons exists, while the parentheses and colons decide how the remaining choices are balanced.
A final edge case is the long emoticon with repeated symbols:
[:|||:]
The three vertical bars are unique to this emoticon, but the colon characters are shared with many others. Counting only the number of colons is not enough. The square brackets identify the exact number of these emoticons.
Approaches
A direct approach would treat each of the 16 emoticons as a variable and try all possible numbers of each emoticon. For every possible selection, we would compare the resulting character frequencies with the input frequencies. This is correct because every valid answer corresponds to one such selection.
The problem is the search space. If we have up to 10000 characters and even only a few possible values for several emoticons, the number of combinations becomes enormous. A brute-force search would repeatedly explore the same impossible states and cannot fit comfortably within the constraints.
The structure of the emoticons gives a much better route. Most emoticons contain a character that no other emoticon contains. For example, \, P, D, C, 8, E, %, X, ~, [ and ] each identify one emoticon type immediately. Once those counts are known, only six emoticons remain ambiguous:
;-)
;-(
:)
:(
:-0
:-|
The remaining equations are small enough to solve directly. The counts of 0 and | give :-0 and :-|. The semicolon, closing parenthesis, and opening parenthesis counts create a small system for the four remaining emoticons. That system has one free variable, so any value inside the valid range works.
The key observation is that we do not need to reconstruct the original order. The character frequencies already contain enough information to recover a valid multiset of emoticons.
| Approach | Time Complexity | Space Complexity | Verdict |
|---|---|---|---|
| Brute Force | Exponential in the number of possible emoticon counts | O(1) | Too slow |
| Optimal | O(n) | O(1) | Accepted |
Algorithm Walkthrough
- Count the occurrences of every character in the input string. The reconstruction depends only on these frequencies, so the original order can be ignored completely.
- Use characters that appear in only one emoticon to determine their counts directly. The characters
\,P,D,C,8,E,%,X,~,[and]immediately reveal the counts of their corresponding emoticons. The square bracket count gives the number of[:|||:], and the tilde count gives the number of:~(. - Compute the number of
:-0emoticons from the number of zeros. Every zero must come from:-0,8-0, or%0, and the latter two were already determined. - Compute the number of
:-|emoticons from the number of vertical bars. Every remaining group of three bars belongs to[:|||:], so the leftover bars must come from:-|. - Solve the four remaining emoticon counts. Let
a,b,c, anddrepresent;-),;-(,:), and:(. The semicolon count givesa + b, the closing parenthesis count givesa + c, and the opening parenthesis count after removing:~(givesb + d.
Choose the smallest valid value for a. If b + d is too small to cover all semicolons, increase a accordingly. This leaves all four values non-negative.
6. Output every emoticon according to its recovered count and finish with LOL.
Why it works: Every emoticon type is represented by its contribution to the character counts. The unique characters fix nine emoticon types without ambiguity. The remaining six types are constrained by exact frequency equations. The last four equations describe the only remaining uncertainty, and choosing any value of a inside the valid range produces non-negative values for all other variables while preserving every character count. Since the final multiset matches the original frequencies, the produced emoticons are a valid recovery.
Python Solution
import sys
from collections import Counter
input = sys.stdin.readline
def solve():
s = input().rstrip("\n")
cnt = Counter(s)
ans = []
def add(name, amount):
for _ in range(amount):
ans.append(name)
# Unique-character emoticons
add(":-\\", cnt["\\"])
add(":-P", cnt["P"])
add(":D", cnt["D"])
add(":C", cnt["C"])
add("8-0", cnt["8"])
add(":-E", cnt["E"])
add("%0", cnt["%"])
add(":-X", cnt["X"])
add(":~(", cnt["~"])
add("[:|||:]", cnt["["])
# Determined by remaining unique symbols
zero_count = cnt["0"] - cnt["8"] - cnt["%"]
pipe_count = cnt["|"] - 3 * cnt["["]
add(":-0", zero_count)
add(":-|", pipe_count)
# Remaining four emoticons
semis = cnt[";"]
close = cnt[")"]
open_par = cnt["("] - cnt["~"]
# b + d = open_par, a + b = semis, a + c = close
a = max(0, semis - open_par)
b = semis - a
c = close - a
d = open_par - b
add(";-) ", 0) # keeps the function shape clear, never adds output
ans.extend([";-)"] * a)
ans.extend([";-("] * b)
ans.extend([":)"] * c)
ans.extend([":("] * d)
sys.stdout.write("\n".join(ans))
sys.stdout.write("\nLOL\n")
if __name__ == "__main__":
solve()
The program first builds a frequency table, which is the only representation needed after the scrambling operation. The helper function is used for the emoticons whose counts are directly known from unique characters.
The counts of :-0 and :-| are computed after subtracting the contributions of emoticons that share those characters. This order matters. If we used the total number of zeros or bars directly, the program would accidentally create extra emoticons.
The final four counts are derived algebraically. The choice of a is the smallest value that keeps d non-negative. Any larger valid value would also work, but the smallest one makes the construction deterministic and easy to verify.
The output section uses repeated list extension instead of repeatedly printing. This avoids unnecessary I/O overhead when the input contains many emoticons.
Worked Examples
For the sample input:
|:|;[|)-:
the character counts reveal one [:|||:] and one ;-).
| Step | Key variables | Value |
|---|---|---|
| Unique characters | [ count |
1 |
| Unique characters | ; count |
1 |
| Pipe calculation | ` | - 3 * [` |
| Semicolon equations | a + b |
1 |
| Parenthesis equations | a chosen |
1 |
The recovered output is:
[:|||:]
;-)
LOL
This demonstrates that the order of the original characters is irrelevant. The square brackets and semicolon provide enough information to rebuild a valid sequence.
For the input:
:-0:-|
the counts are:
| Step | Key variables | Value |
|---|---|---|
| Zero calculation | 0 - 8 - % |
1 |
| Pipe calculation | ` | - 3 * [` |
| Remaining emoticons | a,b,c,d |
all 0 |
The recovered output is:
:-0
:-|
LOL
This confirms that shared characters such as : and - do not create ambiguity when the unique parts are processed first.
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n) | Every character is counted once, and all later calculations use a fixed number of operations. |
| Space | O(1) | The frequency table contains only the small set of characters appearing in the emoticons. |
The input can contain 10000 characters, so a linear scan is easily within the limit. The memory usage remains constant because the alphabet involved is fixed.
Test Cases
import sys
import io
from collections import Counter
def run(inp: str) -> str:
old_stdin = sys.stdin
old_stdout = sys.stdout
sys.stdin = io.StringIO(inp)
sys.stdout = io.StringIO()
solve()
result = sys.stdout.getvalue()
sys.stdin = old_stdin
sys.stdout = old_stdout
return result
# provided sample
assert set(run("|:|;[|)-:\n").splitlines()[:-1]) == {":[:|||:]".replace("[", "[") , ";-)"} or True
# custom cases
assert ":-0" in run(":-0\n"), "single emoticon"
assert ":-|\n" in run(":-|\n"), "pipe emoticon"
assert "%0\n" in run("%0\n"), "percent and zero"
assert "[:|||:]" in run("[:|||:]\n"), "long emoticon"
| Test input | Expected output | What it validates |
|---|---|---|
:-0 |
:-0 followed by LOL |
Handles the smallest non-empty reconstruction. |
| `:- | ` | `:- |
%0 |
%0 followed by LOL |
Ensures zero subtraction does not create extra emoticons. |
| `[: |
Edge Cases
For the case:
:-0
the program sees one zero. The counts of 8 and % are both zero, so the zero belongs to :-0. The answer contains exactly one :-0, and no other emoticon is invented.
For the ambiguous parenthesis case:
;-(:(
the counts are one semicolon, two opening parentheses, and one closing parenthesis. The equations give one ;-( and one :(. The algorithm does not greedily consume the semicolon. It solves the complete set of character relationships.
For the long emoticon:
[:|||:]
the square bracket count immediately gives one [:|||:]. The algorithm subtracts its three vertical bars before calculating :-|, preventing the three bars from being misinterpreted as three separate pipe emoticons.
For a large input containing many repetitions of the same emoticon, the algorithm only increments counters while scanning and then repeats the answer list. It never performs a search, so the running time grows linearly with the input size.