Typescript Coding Chronicles: String Compression

__zamora__

Zamora

Posted on July 11, 2024

Typescript Coding Chronicles: String Compression

Problem Statement:

Given an array of characters chars, compress it using the following algorithm:

  • Begin with an empty string s.
  • For each group of consecutive repeating characters in chars:
    • If the group's length is 1, append the character to s.
    • Otherwise, append the character followed by the group's length.

The compressed string s should not be returned separately, but instead, be stored in the input character array chars. Note that group lengths that are 10 or longer will be split into multiple characters in chars.

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

You must write an algorithm that uses only constant extra space.

Example 1:

  • Input: chars = ["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: The groups are "aa", "bb", and "ccc". This compresses to "a2b2c3".

Example 2:

  • Input: chars = ["a"]
  • Output: Return 1, and the first character of the input array should be: ["a"]
  • Explanation: The only group is "a", which remains uncompressed since it's a single character.

Example 3:

  • Input: chars = ["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: The groups are "a" and "bbbbbbbbbbbb". This compresses to "ab12".

Constraints:

  • 1 <= chars.length <= 2000
  • chars[i] is a lowercase English letter, uppercase English letter, digit, or symbol.

Initial Thought Process:

To solve this problem, we need to iterate through the array while keeping track of the current character and its count. When we encounter a new character, we append the current character and its count (if greater than 1) to the array. We need to ensure that we do this in place to meet the space complexity requirement.

Basic Solution (Brute Force):

The brute force solution involves creating a new array to store the compressed version of the input array. This is not space efficient but helps us understand the steps involved.

Code:

function compressBruteForce(chars: string[]): number {
    const n = chars.length;
    let compressed: string[] = [];
    let i = 0;

    while (i < n) {
        let char = chars[i];
        let count = 0;

        while (i < n && chars[i] === char) {
            i++;
            count++;
        }

        compressed.push(char);
        if (count > 1) {
            compressed.push(...count.toString().split(''));
        }
    }

    for (let j = 0; j < compressed.length; j++) {
        chars[j] = compressed[j];
    }

    return compressed.length;
}
Enter fullscreen mode Exit fullscreen mode

Time Complexity Analysis:

  • Time Complexity: O(n), where n is the length of the array. We iterate through the array once to count characters and once to write the result.
  • Space Complexity: O(n), as we use extra space for the compressed array.

Limitations:

The brute force solution is not space efficient and does not meet the constraint of using only constant extra space.

Optimized Solution:

The optimized solution involves modifying the input array in place to store the compressed version. We use two pointers: one for reading the input array and one for writing the compressed output.

Code:

function compress(chars: string[]): number {
    let writeIndex = 0;
    let i = 0;

    while (i < chars.length) {
        let char = chars[i];
        let count = 0;

        // Count the number of consecutive repeating characters
        while (i < chars.length && chars[i] === char) {
            i++;
            count++;
        }

        // Write the character
        chars[writeIndex] = char;
        writeIndex++;

        // Write the count if greater than 1
        if (count > 1) {
            let countStr = count.toString();
            for (let j = 0; j < countStr.length; j++) {
                chars[writeIndex] = countStr[j];
                writeIndex++;
            }
        }
    }

    return writeIndex;
}
Enter fullscreen mode Exit fullscreen mode

Time Complexity Analysis:

  • Time Complexity: O(n), where n is the length of the array. We iterate through the array once to count characters and once to write the result.
  • Space Complexity: O(1), as we use only a constant amount of extra space for variables.

Improvements Over Basic Solution:

  • The optimized solution reduces space complexity to O(1) by modifying the input array in place.

Edge Cases and Testing:

Edge Cases:

  1. The array contains only one character.
  2. The array contains no consecutive repeating characters.
  3. The array contains a large number of consecutive repeating characters.

Test Cases:

console.log(compressBruteForce(["a","a","b","b","c","c","c"])); // 6, ["a","2","b","2","c","3"]
console.log(compressBruteForce(["a"])); // 1, ["a"]
console.log(compressBruteForce(["a","b","b","b","b","b","b","b","b","b","b","b","b"])); // 4, ["a","b","1","2"]
console.log(compressBruteForce(["a","a","a","a","a","a","a","a","a","a"])); // 3, ["a","1","0"]
console.log(compressBruteForce(["a","b","c"])); // 3, ["a","b","c"]

console.log(compress(["a","a","b","b","c","c","c"])); // 6, ["a","2","b","2","c","3"]
console.log(compress(["a"])); // 1, ["a"]
console.log(compress(["a","b","b","b","b","b","b","b","b","b","b","b","b"])); // 4, ["a","b","1","2"]
console.log(compress(["a","a","a","a","a","a","a","a","a","a"])); // 3, ["a","1","0"]
console.log(compress(["a","b","c"])); // 3, ["a","b","c"]
Enter fullscreen mode Exit fullscreen mode

General Problem-Solving Strategies:

  1. Understand the Problem: Carefully read the problem statement to understand the requirements and constraints.
  2. Identify Key Operations: Determine the key operations needed, such as counting consecutive characters and modifying the array in place.
  3. Optimize for Efficiency: Use efficient algorithms and data structures to minimize time and space complexity.
  4. Test Thoroughly: Test the solution with various cases, including edge cases, to ensure correctness.

Identifying Similar Problems:

  1. String Manipulation:

    • Problems where you need to modify strings based on specific conditions.
    • Example: Removing duplicates from a string.
  2. In-Place Algorithms:

    • Problems where operations need to be performed in place with limited extra space.
    • Example: Removing elements from an array without extra space.

Conclusion:

  • The problem of compressing a character array can be efficiently solved using both a brute force approach and an optimized in-place approach.
  • Understanding the problem and breaking it down into manageable parts is crucial.
  • Using efficient algorithms ensures the solution is optimal for large inputs.
  • Testing with various edge cases ensures robustness.
  • Recognizing patterns in problems can help apply similar solutions to other challenges.

By practicing such problems and strategies, you can improve your problem-solving skills and be better prepared for various coding challenges.

💖 💪 🙅 🚩
__zamora__
Zamora

Posted on July 11, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related