String Compression
String Compression is LeetCode 443 (Medium). You get an array of characters chars. Compress it in place: for each run of equal characters in a row, write the character, then the run's length if it is more than 1.
- A count of 10 or more is split into one cell per digit, so 12 takes two cells,
'1'and'2'. - Return the new length. The judge ignores every cell after it.
- Only constant extra space is allowed, so the result must be written into
charsitself.
- 1 <= chars.length <= 2000
- chars[i] is a lowercase English letter, uppercase English letter, digit, or symbol.
Intuition
Read each run, then write its short form over the front of the same array. This is safe because the short form of a run is never longer than the run itself:
- Run of 1: writes 1 cell, the character.
- Run of 2 to 9: writes 2 cells, the character and one digit.
- Run of 10 or more: writes 1 cell plus one per digit, far fewer than the run.
So the write pointer can never overtake the read pointer. Every write lands on a cell that has already been read, and no character still waiting to be read is lost.
Rewriting an array into itself with two pointers, a slow write and a fast read, is the same pattern as Remove Duplicates from Sorted Array and Move Zeroes. The real work is always showing that write stays behind read.
Approach
Before reading on, work out how many cells a run of 1, a run of 5 and a run of 12 should take. Then check whether writing over the array while you are still reading it can ever destroy a character you have not read. Aim for O(n) time and O(1) space.
Start both pointers at 0
read scans the original characters and write marks the next cell of the compressed result. Both point into the same array, which is how the solution stays within constant extra space.
Measure one run
Remember ch = chars[read], then move read forward while chars[read] == ch, counting the steps in run. The scan stops at the first different character or at the end of the array, so the last run needs no extra code.
Write the character
Put ch into chars[write] and move write on by one. Every run writes its character, and a run of 1 writes nothing else. By now read has moved past the whole run, so this cell is free to overwrite.
Write the count digit by digit
If run > 1, turn it into text with str(run) and write each digit into its own cell, left to right. Going through the string keeps the digits in reading order with no arithmetic.
Return the write position
When read reaches the end, write equals the number of cells the compressed result fills. That is exactly the length the judge expects, and it reads that many cells from the front.
String Compression solution in Python | C++ | Java
Common pitfalls
Putting the whole count in one cell
chars[write] = str(run) write += 1
for d in str(run):
chars[write] = d
write += 1On ["a"] + ["b"] * 12 this stores "12" in a single cell and returns 3. The answer is 4, with '1' and '2' in separate cells.
Writing a count for a run of 1
chars[write] = ch write += 1 for d in str(run): ...
if run > 1:
for d in str(run): ...On ["a","b","c"] the '1' after 'a' lands on cell 1 before 'b' has been read, so the input is destroyed. The correct result is the array unchanged, with length 3.
Peeling digits off with % 10
while run:
chars[write] = str(run % 10)
run //= 10
write += 1for d in str(run):
chars[write] = d
write += 1% 10 gives the last digit first, so twelve 'b's become b, 2, 1. Either reverse those cells afterwards or write the digits from the number's string.
Edge cases
Every run is 1 long, so each character is written back onto its own cell and the answer is the original length.
The result is '2', '3'. It looks odd to a person, but the judge compares cells, so the code needs nothing special.
Complexity
read and written at most once by write. A run is at most 2,000 long, so str(run) has at most 4 characters, which counts as constant space.String Compression FAQ
Is this the same as run-length encoding?
It is run-length encoding with two changes: a run of 1 keeps no count, and the result must overwrite the input instead of being built as a new string. A plain encoder that returns "a1b1" or allocates a new string breaks both rules.
How is String Compression II different?
LeetCode 1531 lets you delete up to k characters first and asks for the shortest encoded length you can reach. That is a dynamic-programming problem over positions and deletions, so the single two-pointer pass here does not apply.