Facebook Pixel

3271. Hash Divided String

MediumStringSimulation
Leetcode Link

Problem Description

You are given a string s of length n and an integer k, where n is a multiple of k. Your goal is to transform the string s into a new compressed string called result that has a length of n / k.

The transformation process works as follows:

  1. Divide the string: Split s into n / k consecutive substrings, where each substring has exactly k characters.

  2. Process each substring: For each substring (processing them in order from left to right):

    • Calculate the hash value of each character, where the hash value is the character's position in the alphabet ('a' → 0, 'b' → 1, 'c' → 2, ..., 'z' → 25)
    • Sum up all the hash values of the characters in the current substring
    • Take the remainder when this sum is divided by 26 (this gives you hashedChar)
    • Convert hashedChar back to a lowercase letter (0 → 'a', 1 → 'b', ..., 25 → 'z')
    • Append this character to the result string
  3. Return the result: After processing all substrings, return the final result string.

For example, if s = "abcd" and k = 2:

  • First substring: "ab" → hash values: 0 + 1 = 1 → 1 % 26 = 1 → 'b'
  • Second substring: "cd" → hash values: 2 + 3 = 5 → 5 % 26 = 5 → 'f'
  • Result: "bf"

The solution iterates through the string in chunks of size k, computes the sum of character positions for each chunk, takes modulo 26, and converts back to a character to build the final result.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The problem is essentially asking us to create a hash function that compresses a string by mapping every k characters to a single character. This is a straightforward simulation problem where we need to follow the given instructions step by step.

The key insight is recognizing that this is a chunking and hashing operation:

  • We're dividing the input into equal-sized chunks
  • Each chunk gets reduced to a single character through a hashing process
  • The hash function is simply the sum of character positions modulo 26

Why does this approach make sense?

  1. Fixed chunk size: Since n is guaranteed to be a multiple of k, we know we can cleanly divide the string into chunks of size k without any remainder. This makes the iteration pattern simple - we can jump by k positions each time.

  2. Character mapping: The problem defines a clear mapping from characters to numbers ('a' → 0, 'b' → 1, etc.). This is just ord(char) - ord('a'), which gives us the alphabetical position.

  3. Modulo operation: Taking the sum modulo 26 ensures our result always maps back to a valid lowercase letter (since there are 26 letters in the alphabet). No matter how large the sum gets, sum % 26 will always be in the range [0, 25].

  4. Reverse mapping: Converting back from a number to a character is the inverse operation: chr(ord('a') + hashedChar).

The solution naturally flows from these observations - we iterate through the string in steps of k, calculate the hash for each substring, and build our result character by character. There's no need for complex data structures or algorithms; it's purely a matter of following the hashing recipe provided in the problem statement.

Solution Approach

The solution implements a direct simulation of the hashing process described in the problem. Let's walk through the implementation step by step:

1. Initialize the result container:

ans = []

We use a list to collect the hashed characters, which will be joined into a string at the end. This is more efficient than string concatenation in Python.

2. Iterate through the string in chunks of size k:

for i in range(0, len(s), k):

The loop starts at index 0 and jumps by k positions each iteration. This ensures we process each substring of length k exactly once. For example, if len(s) = 6 and k = 2, we'll have i values of 0, 2, and 4.

3. Calculate the hash sum for each substring:

t = 0
for j in range(i, i + k):
    t += ord(s[j]) - ord("a")

For each substring starting at position i:

  • We iterate through the next k characters (from index i to i + k - 1)
  • Convert each character to its hash value using ord(s[j]) - ord("a")
    • ord("a") gives 97, ord("b") gives 98, etc.
    • Subtracting ord("a") normalizes these to 0, 1, 2, ... 25
  • Accumulate the sum in variable t

4. Apply modulo and convert to character:

hashedChar = t % 26
ans.append(chr(ord("a") + hashedChar))
  • Take the sum modulo 26 to get a value in range [0, 25]
  • Convert this number back to a character:
    • ord("a") gives the ASCII value of 'a' (97)
    • Adding hashedChar gives us the ASCII value of our target character
    • chr() converts the ASCII value back to a character
  • Append the resulting character to our answer list

5. Build and return the final string:

return "".join(ans)

Join all the hashed characters into a single string and return it.

Time Complexity: O(n) where n is the length of the input string, as we visit each character exactly once.

Space Complexity: O(n/k) for storing the result string, which has length n/k.

The algorithm is straightforward - no complex data structures or advanced techniques are needed. It's a pure simulation that follows the problem's hashing recipe exactly as specified.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through the solution with a concrete example: s = "mauigqpe" and k = 4.

Step 1: Divide into chunks

  • String length: 8 characters
  • Number of chunks: 8 / 4 = 2 chunks
  • Chunks: "maui" and "gqpe"

Step 2: Process first chunk "maui"

  • Calculate hash values:
    • 'm' → 12 (m is the 13th letter, so position 12)
    • 'a' → 0
    • 'u' → 20
    • 'i' → 8
  • Sum: 12 + 0 + 20 + 8 = 40
  • Apply modulo: 40 % 26 = 14
  • Convert to character: 14 → 'o' (since 'a' + 14 = 'o')

Step 3: Process second chunk "gqpe"

  • Calculate hash values:
    • 'g' → 6
    • 'q' → 16
    • 'p' → 15
    • 'e' → 4
  • Sum: 6 + 16 + 15 + 4 = 41
  • Apply modulo: 41 % 26 = 15
  • Convert to character: 15 → 'p' (since 'a' + 15 = 'p')

Step 4: Build result

  • First chunk produced: 'o'
  • Second chunk produced: 'p'
  • Final result: "op"

The algorithm compressed the 8-character string "mauigqpe" into a 2-character string "op" by hashing every 4 consecutive characters into a single character.

Solution Implementation

1class Solution:
2    def stringHash(self, s: str, k: int) -> str:
3        """
4        Hashes a string by dividing it into substrings of length k and converting each substring
5        to a single character based on the sum of character positions modulo 26.
6      
7        Args:
8            s: Input string to be hashed
9            k: Length of each substring
10          
11        Returns:
12            Hashed string where each k characters are reduced to a single character
13        """
14        result = []
15      
16        # Process the string in chunks of size k
17        for i in range(0, len(s), k):
18            # Calculate sum of character positions for current substring
19            substring_sum = 0
20          
21            # Sum up the positions (0-indexed) of each character in the current chunk
22            for j in range(i, i + k):
23                # Convert character to its position (a=0, b=1, ..., z=25)
24                substring_sum += ord(s[j]) - ord('a')
25          
26            # Apply modulo 26 to get the hashed character position
27            hashed_position = substring_sum % 26
28          
29            # Convert position back to character and add to result
30            hashed_character = chr(ord('a') + hashed_position)
31            result.append(hashed_character)
32      
33        # Join all hashed characters into final string
34        return ''.join(result)
35
1class Solution {
2    /**
3     * Hashes a string by dividing it into substrings of length k and 
4     * converting each substring into a single character.
5     * 
6     * @param s The input string to be hashed
7     * @param k The length of each substring
8     * @return The hashed string
9     */
10    public String stringHash(String s, int k) {
11        // StringBuilder to store the resulting hashed string
12        StringBuilder result = new StringBuilder();
13      
14        // Get the length of the input string
15        int stringLength = s.length();
16      
17        // Process the string in chunks of size k
18        for (int startIndex = 0; startIndex < stringLength; startIndex += k) {
19            // Initialize sum for current substring
20            int charSum = 0;
21          
22            // Calculate the sum of character values in the current substring
23            // Each character's value is its position in the alphabet (a=0, b=1, etc.)
24            for (int currentIndex = startIndex; currentIndex < startIndex + k; currentIndex++) {
25                charSum += s.charAt(currentIndex) - 'a';
26            }
27          
28            // Hash the sum by taking modulo 26 to get a value between 0-25
29            int hashedValue = charSum % 26;
30          
31            // Convert the hashed value back to a character and append to result
32            // Add the hashed value to 'a' to get the corresponding character
33            result.append((char) ('a' + hashedValue));
34        }
35      
36        // Return the final hashed string
37        return result.toString();
38    }
39}
40
1class Solution {
2public:
3    string stringHash(string s, int k) {
4        string result;
5        int stringLength = s.length();
6      
7        // Process the string in chunks of size k
8        for (int startIndex = 0; startIndex < stringLength; startIndex += k) {
9            int sum = 0;
10          
11            // Calculate sum of character values in current chunk
12            // Each character's value is its position in alphabet (a=0, b=1, ..., z=25)
13            for (int currentIndex = startIndex; currentIndex < startIndex + k; ++currentIndex) {
14                sum += s[currentIndex] - 'a';
15            }
16          
17            // Hash the sum by taking modulo 26 to get a value between 0-25
18            int hashedValue = sum % 26;
19          
20            // Convert the hashed value back to a character and append to result
21            result += static_cast<char>('a' + hashedValue);
22        }
23      
24        return result;
25    }
26};
27
1/**
2 * Hashes a string by dividing it into segments of length k and converting each segment
3 * @param s - The input string to be hashed (assumed to contain only lowercase letters)
4 * @param k - The length of each segment to process
5 * @returns The hashed string result
6 */
7function stringHash(s: string, k: number): string {
8    // Array to store the hashed characters
9    const hashedCharacters: string[] = [];
10  
11    // Get the total length of the input string
12    const stringLength: number = s.length;
13
14    // Process the string in segments of length k
15    for (let startIndex = 0; startIndex < stringLength; startIndex += k) {
16        // Initialize sum for current segment
17        let segmentSum: number = 0;
18      
19        // Calculate the sum of character values in the current segment
20        // Each character's value is its position in the alphabet (a=0, b=1, ..., z=25)
21        for (let currentIndex = startIndex; currentIndex < startIndex + k; currentIndex++) {
22            segmentSum += s.charCodeAt(currentIndex) - 97; // 97 is ASCII code for 'a'
23        }
24      
25        // Hash the sum by taking modulo 26 to get a value between 0-25
26        const hashedValue: number = segmentSum % 26;
27      
28        // Convert the hashed value back to a lowercase letter and add to result
29        hashedCharacters.push(String.fromCharCode(97 + hashedValue));
30    }
31
32    // Join all hashed characters into a single string
33    return hashedCharacters.join('');
34}
35

Time and Space Complexity

The time complexity is O(n), where n is the length of the string s. The algorithm iterates through the string in chunks of size k, processing n/k chunks total. For each chunk, it performs k character operations (converting to ASCII values and summing). Therefore, the total operations are (n/k) * k = n, resulting in linear time complexity.

The space complexity is O(n/k) for the answer list that stores the hashed characters. Since each chunk of k characters produces one hashed character, the answer list contains n/k elements. If we ignore the space consumption of the answer string (as mentioned in the reference), the space complexity would be O(1) as we only use a constant amount of extra space for variables t, i, and j.

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Off-by-One Errors in Character Position Calculation

A common mistake is incorrectly calculating the character's position in the alphabet. Developers might forget to subtract ord('a') or might use 1-based indexing instead of 0-based.

Incorrect Implementation:

# Wrong: Using 1-based indexing
substring_sum += ord(s[j]) - ord('a') + 1  # 'a' would be 1, not 0

# Wrong: Forgetting to normalize
substring_sum += ord(s[j])  # Would give ASCII values like 97, 98, etc.

Solution: Always remember that the problem specifies 0-based positions ('a' → 0), so use ord(s[j]) - ord('a') consistently.

2. Integer Overflow in Languages with Fixed Integer Sizes

While Python handles arbitrary precision integers automatically, in languages like Java or C++, accumulating large sums might cause integer overflow if the substring is very long or contains many 'z' characters.

Potential Issue in Other Languages:

// In Java, this could overflow for large k values
int sum = 0;
for (int j = i; j < i + k; j++) {
    sum += s.charAt(j) - 'a';  // Could overflow if k is very large
}

Solution: Apply modulo 26 during accumulation to prevent overflow:

substring_sum = 0
for j in range(i, i + k):
    substring_sum = (substring_sum + ord(s[j]) - ord('a')) % 26

3. Incorrect Loop Boundaries

Developers might incorrectly set up the inner loop boundaries, especially when dealing with the end index.

Incorrect Implementation:

# Wrong: Off by one - would include k+1 characters
for j in range(i, i + k + 1):
    substring_sum += ord(s[j]) - ord('a')

# Wrong: Would miss the last character
for j in range(i, i + k - 1):
    substring_sum += ord(s[j]) - ord('a')

Solution: Use range(i, i + k) which correctly iterates from index i to i + k - 1, giving exactly k characters.

4. String Concatenation Performance

Using string concatenation in a loop can be inefficient in Python due to string immutability.

Inefficient Implementation:

result = ""
for i in range(0, len(s), k):
    # ... calculate hashed_character
    result += hashed_character  # Creates a new string object each time

Solution: Use a list to collect characters and join at the end:

result = []
for i in range(0, len(s), k):
    # ... calculate hashed_character
    result.append(hashed_character)
return ''.join(result)

5. Assuming Input Validity

The problem states that n is a multiple of k, but in production code, you might want to validate this assumption.

Defensive Programming:

def stringHash(self, s: str, k: int) -> str:
    if len(s) % k != 0:
        raise ValueError(f"String length {len(s)} is not divisible by k={k}")
  
    # Rest of the implementation...

6. Misunderstanding the Modulo Operation

Some developers might apply modulo 26 at the wrong step or forget it entirely.

Incorrect Implementation:

# Wrong: Applying modulo to each character individually
for j in range(i, i + k):
    substring_sum += (ord(s[j]) - ord('a')) % 26  # Unnecessary here

# Wrong: Forgetting modulo entirely
hashed_position = substring_sum  # Could be > 25, causing chr() to produce non-lowercase letters

Solution: Apply modulo 26 only once after summing all characters in the substring:

hashed_position = substring_sum % 26
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

What's the output of running the following function using input [30, 20, 10, 100, 33, 12]?

1def fun(arr: List[int]) -> List[int]:
2    import heapq
3    heapq.heapify(arr)
4    res = []
5    for i in range(3):
6        res.append(heapq.heappop(arr))
7    return res
8
1public static int[] fun(int[] arr) {
2    int[] res = new int[3];
3    PriorityQueue<Integer> heap = new PriorityQueue<>();
4    for (int i = 0; i < arr.length; i++) {
5        heap.add(arr[i]);
6    }
7    for (int i = 0; i < 3; i++) {
8        res[i] = heap.poll();
9    }
10    return res;
11}
12
1class HeapItem {
2    constructor(item, priority = item) {
3        this.item = item;
4        this.priority = priority;
5    }
6}
7
8class MinHeap {
9    constructor() {
10        this.heap = [];
11    }
12
13    push(node) {
14        // insert the new node at the end of the heap array
15        this.heap.push(node);
16        // find the correct position for the new node
17        this.bubble_up();
18    }
19
20    bubble_up() {
21        let index = this.heap.length - 1;
22
23        while (index > 0) {
24            const element = this.heap[index];
25            const parentIndex = Math.floor((index - 1) / 2);
26            const parent = this.heap[parentIndex];
27
28            if (parent.priority <= element.priority) break;
29            // if the parent is bigger than the child then swap the parent and child
30            this.heap[index] = parent;
31            this.heap[parentIndex] = element;
32            index = parentIndex;
33        }
34    }
35
36    pop() {
37        const min = this.heap[0];
38        this.heap[0] = this.heap[this.size() - 1];
39        this.heap.pop();
40        this.bubble_down();
41        return min;
42    }
43
44    bubble_down() {
45        let index = 0;
46        let min = index;
47        const n = this.heap.length;
48
49        while (index < n) {
50            const left = 2 * index + 1;
51            const right = left + 1;
52
53            if (left < n && this.heap[left].priority < this.heap[min].priority) {
54                min = left;
55            }
56            if (right < n && this.heap[right].priority < this.heap[min].priority) {
57                min = right;
58            }
59            if (min === index) break;
60            [this.heap[min], this.heap[index]] = [this.heap[index], this.heap[min]];
61            index = min;
62        }
63    }
64
65    peek() {
66        return this.heap[0];
67    }
68
69    size() {
70        return this.heap.length;
71    }
72}
73
74function fun(arr) {
75    const heap = new MinHeap();
76    for (const x of arr) {
77        heap.push(new HeapItem(x));
78    }
79    const res = [];
80    for (let i = 0; i < 3; i++) {
81        res.push(heap.pop().item);
82    }
83    return res;
84}
85

Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More