String Compression

Question

Given an array of characters, compress it in-place.

The length after compression must always be smaller than or equal to the original array.

Every element of the array should be a character (not int) of length 1.

After you are done modifying the input array in-place, return the new length of the array.

Follow up:

Could you solve it using only O(1) extra space?

Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
Input:
["a","a","b","b","c","c","c"]

Output:
Return 6, and the first 6 characters of the input array should be: ["a","2","b","2","c","3"]

Explanation:
"aa" is replaced by "a2". "bb" is replaced by "b2". "ccc" is replaced by "c3".


Input:
["a"]

Output:
Return 1, and the first 1 characters of the input array should be: ["a"]

Explanation:
Nothing is replaced.

Input:
["a","b","b","b","b","b","b","b","b","b","b","b","b"]

Output:
Return 4, and the first 4 characters of the input array should be: ["a","b","1","2"].

Explanation:
Since the character "a" does not repeat, it is not compressed. "bbbbbbbbbbbb" is replaced by "b12".
Notice each digit has it's own entry in the array.

Solution

We use count and say strategy to solve this problem, which means look for the next position to see if it equals. Also we use two pointer, one moves faster, another to mark the position actually arrived. In addition, we have to care about the case 2 (just contain one element, which means no number we need to add).

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// time:O(n) space:O(1)
public int compress(char[] chars) {
if (chars == null || chars.length == 0) return 0;
int i = 0, j = 0;
while (j < chars.length) {
int count = 1;
char ch = chars[j];
while (j + 1 < chars.length && chars[j] == chars[j + 1]) {
count++;
j++;
}
chars[i++] = ch;
if (count != 1) {
for (char c : String.valueOf(count).toCharArray()) chars[i++] = c;
}
j++;
}
return i;
}