Browse curriculum

String Compression

Compress an array of characters in place by replacing each run of a repeated letter with the letter and its count, using a read and a write pointer.

Problem Understanding

String Compression (LeetCode 443): given an array of characters chars, compress it in place. For each group of consecutive repeating characters, write the character, followed by the group's length if it is more than 1; lengths of 10 or more take several cells. Return the new length, using only O(1) extra space.

Examples: [a,a,b,b,c,c,c] → 6, [a,2,b,2,c,3]; [a] → 1; [a,b,b,b,b,b,b,b,b,b,b,b,b] → 4, [a,b,1,2].

The Intuition: Read Ahead, Write Behind

The compressed form is never longer than the input, so it can overwrite the array from the front.

  1. read scans to the end of the current run.
  2. write puts down the character and, if the run is longer than 1, the digits of its length.
  3. Repeat until read reaches the end.

It is the read/write pointer pair from Remove Duplicates from Sorted Array, writing a count where that lesson writes nothing.

Interactive Walkthrough

w = WRITE · r = READ

SCAN A RUN

chars (rewritten in place)

w

a

0

a

1

r

b

2

b

3

c

4

c

5

c

6

read

2

write

0

Run length

2

 

read scans a run of 'a' from 0 to 1: length 2

Next

Write 'a' at 0

Free preview: the first 3 of 10 steps

chars

Green cells are the compressed output so far, the outlined cells are the run read just scanned, and the filled cell is the one just written. Try Example 3 to see a two-digit count.

Dry Run Table

Input: [a, a, b, b, c, c, c].

Run Length Writes write after
a at 0 to 1 2 a, 2 2
b at 2 to 3 2 b, 2 4
c at 4 to 6 3 c, 3 6

Answer: 6; the array starts [a, 2, b, 2, c, 3].

The Solution Template

An outer loop per run, an inner loop to find its end, and the count written digit by digit.

String Compression Code
1var compress = function(chars) {
2 let read = 0, write = 0;
3 while (read < chars.length) {
4 const ch = chars[read], start = read;
5 while (read < chars.length && chars[read] === ch) read++;
6 chars[write++] = ch;
7 if (read - start > 1) {
8 for (const digit of String(read - start)) {
9 chars[write++] = digit;
10 }
11 }
12 }
13 return write;
14};

Edge Cases & Common Mistakes

  • Runs of length 1: write only the character, no "1".
  • Counts of 10 or more: write each digit in its own cell.
  • Returning the array: the answer is the new length, write.
  • Building a new string: uses O(n) space; the problem asks for O(1) extra.

Summary

Scan each run with read, then write the character and, for runs longer than 1, the digits of the count. The output never outgrows the input, so it fits in place. O(n) time, O(1) space.

More practice: DSA interview questions with answers · patterns cheat sheet

The approach, step by step

  1. Scan a run

    Advance read past every copy of the current character.

  2. Write it

    Write the character at write, then, if the run is longer than 1, each digit of its length.

  3. Return

    Repeat until read reaches the end, and return write.

Frequently asked questions

Why can the write pointer never overtake the read pointer?

A run of length L is written as 1 cell when L = 1, and as 1 + digits(L) cells otherwise. For L ≥ 2, 1 + digits(L) ≤ L (2 for L = 2, 3 for L = 10, and so on), so each run's output fits in the cells the run itself occupied. write always stays at or behind read.


How are counts of 10 or more written?

One cell per digit. A run of twelve b's becomes 'b', '1', '2'. That is why the count is turned into a string and written digit by digit.


What does the function return?

The new length, write. LeetCode checks only the first write characters of the array; whatever is left after them is ignored.

New to the technique behind this problem? Watch it run step by step:

Two Pointers Visualization

Applying for software engineering roles? Free, no sign-up:

AI resume checker: match your resume to the job description