A clear explanation of reformatting a license key by removing dashes, uppercasing characters, and grouping from the right.
Problem Restatement
We are given a string s representing a license key and an integer k.
The string contains letters, digits, and dashes.
We need to reformat it so that:
| Rule | Meaning |
|---|---|
| Remove old dashes | Existing dashes do not matter |
| Convert letters | All letters become uppercase |
| Regroup characters | Groups are separated by dashes |
| Group size | Every group has exactly k characters, except maybe the first |
| First group | It may be shorter than k, but must contain at least one character |
For example:
s = "5F3Z-2e-9-w"
k = 4After removing dashes and uppercasing:
5F3Z2E9WGroup from the right into chunks of size 4:
5F3Z 2E9WAnswer:
"5F3Z-2E9W"Input and Output
| Item | Meaning |
|---|---|
| Input | String s, integer k |
| Output | Reformatted license key |
| Characters | English letters, digits, and dashes |
| Constraint | 1 <= s.length <= 10^5, 1 <= k <= 10^4 |
Function shape:
def licenseKeyFormatting(s: str, k: int) -> str:
...Examples
Example 1:
s = "5F3Z-2e-9-w"
k = 4Remove dashes:
5F3Z2e9wUppercase:
5F3Z2E9WGroup into size 4 from the right:
5F3Z-2E9WAnswer:
"5F3Z-2E9W"Example 2:
s = "2-5g-3-J"
k = 2Remove dashes:
25g3JUppercase:
25G3JGroup from the right:
2-5G-3JAnswer:
"2-5G-3J"First Thought: Clean Then Group
A direct solution has two phases.
First, build a clean string by removing dashes and converting letters to uppercase.
clean = []
for ch in s:
if ch != "-":
clean.append(ch.upper())Then insert dashes so that groups from the right have length k.
This works, but inserting from the front can be awkward because the first group may be shorter.
Key Insight
Groups are easiest to build from the right.
Every group except the first must contain exactly k characters. That means if we scan the cleaned characters from right to left, we can add a dash after every k characters.
For example:
5F3Z2E9WRead from the right:
W 9 E 2 | Z 3 F 5After every 4 characters, insert a dash.
Then reverse the final result:
5F3Z-2E9WThis avoids separately computing the first group size.
Algorithm
Create an empty list ans.
Set count = 0.
Scan s from right to left.
For each character:
- Skip it if it is a dash.
- If
count > 0andcount % k == 0, append a dash toans. - Append the uppercase version of the character.
- Increment
count.
At the end, reverse ans and join it into a string.
Correctness
The algorithm ignores all original dashes, so old grouping has no effect on the result.
It processes all non-dash characters from right to left. Since every complete group except possibly the first must contain k characters, adding a dash after every k processed characters creates exactly the required grouping from the right.
The condition:
count > 0 and count % k == 0adds a dash only between groups. It never adds a dash before the first processed character from the right.
Every appended letter is converted to uppercase, and digits stay unchanged.
After reversing the built list, the character order becomes the original left-to-right order, with dashes inserted between valid groups. Therefore, the returned string satisfies all formatting rules.
Complexity
| Metric | Value | Why |
|---|---|---|
| Time | O(n) | We scan the string once and reverse the result once |
| Space | O(n) | The output list stores the reformatted key |
Implementation
class Solution:
def licenseKeyFormatting(self, s: str, k: int) -> str:
ans = []
count = 0
for ch in reversed(s):
if ch == "-":
continue
if count > 0 and count % k == 0:
ans.append("-")
ans.append(ch.upper())
count += 1
return "".join(reversed(ans))Code Explanation
The result is built in reverse:
ans = []The variable count tracks how many real characters have been added, excluding dashes.
count = 0We scan from right to left:
for ch in reversed(s):Old dashes are skipped:
if ch == "-":
continueBefore adding a new character, we check whether the previous group already has k characters:
if count > 0 and count % k == 0:
ans.append("-")Then we add the uppercase character:
ans.append(ch.upper())Finally, reverse the result because we built it from right to left:
return "".join(reversed(ans))Testing
def run_tests():
s = Solution()
assert s.licenseKeyFormatting("5F3Z-2e-9-w", 4) == "5F3Z-2E9W"
assert s.licenseKeyFormatting("2-5g-3-J", 2) == "2-5G-3J"
assert s.licenseKeyFormatting("abc", 3) == "ABC"
assert s.licenseKeyFormatting("abc", 2) == "A-BC"
assert s.licenseKeyFormatting("--a-a-a-a--", 2) == "AA-AA"
assert s.licenseKeyFormatting("----", 3) == ""
print("all tests passed")
run_tests()Test meaning:
| Test | Why |
|---|---|
"5F3Z-2e-9-w", 4 | Main example |
"2-5g-3-J", 2 | First group shorter than k |
"abc", 3 | One full group |
"abc", 2 | Short first group |
"--a-a-a-a--", 2 | Many old dashes |
"----", 3 | No real characters after removing dashes |