From bc4799507100bc0425dbfd1555ee3dde47ffeec0 Mon Sep 17 00:00:00 2001 From: Andrey Savelev Date: Sun, 23 Jul 2023 22:18:23 +0300 Subject: [PATCH] feat: add string compression solution --- README.md | 6 ++ typescript/src/stringCompression/README.md | 80 +++++++++++++++++++ .../stringCompression.test.ts | 66 +++++++++++++++ .../stringCompression/stringCompression.ts | 30 +++++++ 4 files changed, 182 insertions(+) create mode 100644 typescript/src/stringCompression/README.md create mode 100644 typescript/src/stringCompression/stringCompression.test.ts create mode 100644 typescript/src/stringCompression/stringCompression.ts diff --git a/README.md b/README.md index eb821ac..86b3fd1 100644 --- a/README.md +++ b/README.md @@ -8,3 +8,9 @@ | 2 | [Move Zeroes](https://leetcode.com/problems/move-zeroes/) | [TypeScript](https://github.com/sandrig/leetcode/blob/master/typescript/src/moveZeroes/README.md) | _O(n)_ | _O(1)_ | Array, Two Pointers | | 3 | [Valid Palindrome](https://leetcode.com/problems/valid-palindrome/) | [TypeScript](https://github.com/sandrig/leetcode/blob/master/typescript/src/validPalindrome/README.md) | _O(n)_ | _O(n)_ | String, Two Pointers | | 4 | [Summary Ranges](https://leetcode.com/problems/summary-ranges/) | [TypeScript](https://github.com/sandrig/leetcode/blob/master/typescript/src/summaryRanges/README.md) | _O(n)_ | _O(n)_ | Array | + +## Medium + +| # | Problem | Solution | Time | Space | Tag | +| --- | ----------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | ------ | ------ | -------------------- | +| 1 | [String Compression](https://leetcode.com/problems/string-compression/) | [TypeScript](https://github.com/sandrig/leetcode/blob/master/typescript/src/stringCompression/README.md) | _O(n)_ | _O(n)_ | Two Pointers, String | diff --git a/typescript/src/stringCompression/README.md b/typescript/src/stringCompression/README.md new file mode 100644 index 0000000..395afb3 --- /dev/null +++ b/typescript/src/stringCompression/README.md @@ -0,0 +1,80 @@ +# [String Compression](https://leetcode.com/problems/string-compression/) + +## Description + +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". +``` + +## Solution + +```typescript +function compress(chars: string[]): number { + let writeIndex = 0; // Index to write compressed characters + let readIndex = 0; // Index to read source characters + + while (readIndex < chars.length) { + const currentChar = chars[readIndex]; + let count = 0; + + // Counting the number of repeated characters + while (readIndex < chars.length && chars[readIndex] === currentChar) { + readIndex++; + count++; + } + + // Write a compressed character + chars[writeIndex] = currentChar; + writeIndex++; + + // If the character is repeated more than once, record the number of repetitions + if (count > 1) { + for (const digit of count.toString().split("")) { + chars[writeIndex] = digit; + writeIndex++; + } + } + } + + // Returning the new length of the compressed string + return writeIndex; +} +``` + +## Complexity Analysis + +- Time complexity: _O(n)_ +- Space complexity: _O(n)_ diff --git a/typescript/src/stringCompression/stringCompression.test.ts b/typescript/src/stringCompression/stringCompression.test.ts new file mode 100644 index 0000000..0241e1e --- /dev/null +++ b/typescript/src/stringCompression/stringCompression.test.ts @@ -0,0 +1,66 @@ +import { compress } from "./stringCompression"; + +describe("String Compression", () => { + it("should compress the string with repeated characters", () => { + const chars: string[] = ["a", "a", "b", "b", "c", "c", "c"]; + const compressedLength = compress(chars); + expect(compressedLength).toBe(6); + expect(chars.slice(0, compressedLength)).toEqual([ + "a", + "2", + "b", + "2", + "c", + "3", + ]); + }); + + it("should return the original string if it cannot be compressed", () => { + const chars: string[] = ["a"]; + const compressedLength = compress(chars); + expect(compressedLength).toBe(1); + expect(chars.slice(0, compressedLength)).toEqual(["a"]); + }); + + it("should handle multiple repeating characters", () => { + const chars: string[] = [ + "a", + "b", + "b", + "b", + "b", + "b", + "b", + "b", + "b", + "b", + "b", + "b", + "b", + ]; + const compressedLength = compress(chars); + expect(compressedLength).toBe(4); + expect(chars.slice(0, compressedLength)).toEqual(["a", "b", "1", "2"]); + }); + + it("should handle alternating characters", () => { + const chars: string[] = ["a", "a", "a", "b", "b", "a", "a"]; + const compressedLength = compress(chars); + expect(compressedLength).toBe(6); + expect(chars.slice(0, compressedLength)).toEqual([ + "a", + "3", + "b", + "2", + "a", + "2", + ]); + }); + + it("should handle an empty string", () => { + const chars: string[] = []; + const compressedLength = compress(chars); + expect(compressedLength).toBe(0); + expect(chars.slice(0, compressedLength)).toEqual([]); + }); +}); diff --git a/typescript/src/stringCompression/stringCompression.ts b/typescript/src/stringCompression/stringCompression.ts new file mode 100644 index 0000000..1a81ade --- /dev/null +++ b/typescript/src/stringCompression/stringCompression.ts @@ -0,0 +1,30 @@ +export function compress(chars: string[]): number { + let writeIndex = 0; // Index to write compressed characters + let readIndex = 0; // Index to read source characters + + while (readIndex < chars.length) { + const currentChar = chars[readIndex]; + let count = 0; + + // Counting the number of repeated characters + while (readIndex < chars.length && chars[readIndex] === currentChar) { + readIndex++; + count++; + } + + // Write a compressed character + chars[writeIndex] = currentChar; + writeIndex++; + + // If the character is repeated more than once, record the number of repetitions + if (count > 1) { + for (const digit of count.toString().split("")) { + chars[writeIndex] = digit; + writeIndex++; + } + } + } + + // Returning the new length of the compressed string + return writeIndex; +}