Facebook Pixel

2287. Rearrange Characters to Make Target String

EasyHash TableStringCounting
Leetcode Link

Problem Description

You are given two strings s and target, both indexed starting from 0. Your task is to determine how many complete copies of the string target can be formed using the letters available in string s.

The key points to understand:

  • You can take any letters from s and rearrange them in any order
  • Each letter in s can only be used once
  • You need to form complete copies of target - partial copies don't count
  • You want to find the maximum number of complete copies possible

For example, if s = "ilovecodingonleetcode" and target = "code", you need to check:

  • How many 'c's are available in s versus needed in target
  • How many 'o's are available in s versus needed in target
  • How many 'd's are available in s versus needed in target
  • How many 'e's are available in s versus needed in target

The limiting factor will be the character that has the smallest ratio of available letters to required letters. If s has 6 'e's and target needs 1 'e' per copy, you could make 6 copies based on 'e' alone. But if s only has 2 'd's and target needs 1 'd' per copy, you can only make 2 complete copies of target.

The solution counts the frequency of each character in both strings, then for each character in target, calculates how many times it can be used (by dividing available count by required count), and returns the minimum of these values.

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

Intuition

Think of this problem like a recipe. If you want to make cookies and the recipe calls for 2 cups of flour, 1 cup of sugar, and 3 eggs, how many batches can you make with your available ingredients? You'd check each ingredient: maybe you have 8 cups of flour (enough for 4 batches), 5 cups of sugar (enough for 5 batches), but only 6 eggs (enough for 2 batches). The eggs become your bottleneck - you can only make 2 complete batches.

The same logic applies here. Each character in target is like an ingredient, and we need to figure out which character limits us the most.

We start by counting how many of each character we have in s (our pantry) and how many of each character we need for one copy of target (our recipe).

For each unique character in target, we calculate: "How many times can I fulfill this character's requirement?" This is simply count_in_s // count_in_target. The integer division gives us the maximum number of complete copies we can make based on just that character.

The key insight is that we need ALL characters to form a complete copy of target. Just like in cooking, having excess of one ingredient doesn't help if another ingredient runs out. Therefore, the character that allows us to make the fewest copies determines our final answer.

This naturally leads us to take the minimum across all these ratios: min(cnt1[c] // cnt2[c] for each c in target). This minimum value represents the maximum number of complete copies of target we can form.

Solution Approach

The implementation uses a counting approach with hash maps to efficiently solve this problem.

Step 1: Count character frequencies

We use Python's Counter from the collections module to count the frequency of each character:

  • cnt1 = Counter(s) creates a dictionary-like object where keys are characters from s and values are their frequencies
  • cnt2 = Counter(target) does the same for the target string

For example, if s = "aabbcc", then cnt1 = {'a': 2, 'b': 2, 'c': 2}

Step 2: Calculate maximum copies for each character

For each character c that appears in target (with frequency v), we calculate how many complete copies we can make:

  • cnt1[c] // v gives us the maximum number of times we can use character c
  • The integer division // ensures we only count complete copies

If a character in target doesn't exist in s, Counter returns 0 by default, which results in 0 // v = 0, correctly indicating we can't form any copies.

Step 3: Find the limiting factor

We use min() to find the smallest ratio across all characters in target:

return min(cnt1[c] // v for c, v in cnt2.items())

This generator expression iterates through each character-frequency pair (c, v) in cnt2, calculates the ratio, and returns the minimum value.

Time Complexity: O(m + n) where m is the length of s and n is the length of target, as we need to count characters in both strings.

Space Complexity: O(1) since we're only storing character counts and there are at most 26 lowercase English letters (constant space).

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 a concrete example to illustrate the solution approach.

Example: s = "abbaccaddeebc" and target = "abcd"

Step 1: Count character frequencies

First, we count how many times each character appears in both strings:

  • s = "abbaccaddeebc"cnt1 = {'a': 3, 'b': 3, 'c': 3, 'd': 2, 'e': 2}
  • target = "abcd"cnt2 = {'a': 1, 'b': 1, 'c': 1, 'd': 1}

Step 2: Calculate maximum copies for each character

Now we check how many copies of target we can make based on each character:

  • Character 'a': We have 3 'a's in s, need 1 'a' per copy of target

    • Maximum copies based on 'a': 3 // 1 = 3
  • Character 'b': We have 3 'b's in s, need 1 'b' per copy of target

    • Maximum copies based on 'b': 3 // 1 = 3
  • Character 'c': We have 3 'c's in s, need 1 'c' per copy of target

    • Maximum copies based on 'c': 3 // 1 = 3
  • Character 'd': We have 2 'd's in s, need 1 'd' per copy of target

    • Maximum copies based on 'd': 2 // 1 = 2

Step 3: Find the limiting factor

The minimum of all these ratios is: min(3, 3, 3, 2) = 2

The character 'd' is our bottleneck - we can only make 2 complete copies of "abcd" because we only have 2 'd's available.

Verification:

  • First copy uses: 1 'a', 1 'b', 1 'c', 1 'd' (remaining: 2 'a's, 2 'b's, 2 'c's, 1 'd')
  • Second copy uses: 1 'a', 1 'b', 1 'c', 1 'd' (remaining: 1 'a', 1 'b', 1 'c', 0 'd's)
  • Cannot make a third copy because we're out of 'd's

Therefore, the answer is 2.

Solution Implementation

1from collections import Counter
2
3class Solution:
4    def rearrangeCharacters(self, s: str, target: str) -> int:
5        # Count frequency of each character in the source string
6        source_char_count = Counter(s)
7      
8        # Count frequency of each character in the target string
9        target_char_count = Counter(target)
10      
11        # Calculate the maximum number of times we can form the target string
12        # For each character in target, divide source count by target count
13        # The minimum ratio determines how many complete target strings we can form
14        max_copies = min(source_char_count[char] // count 
15                        for char, count in target_char_count.items())
16      
17        return max_copies
18
1class Solution {
2    public int rearrangeCharacters(String s, String target) {
3        // Array to store frequency of each character in string s
4        int[] sourceCharCount = new int[26];
5        // Array to store frequency of each character in target string
6        int[] targetCharCount = new int[26];
7      
8        // Count frequency of each character in source string s
9        for (int i = 0; i < s.length(); i++) {
10            sourceCharCount[s.charAt(i) - 'a']++;
11        }
12      
13        // Count frequency of each character in target string
14        for (int i = 0; i < target.length(); i++) {
15            targetCharCount[target.charAt(i) - 'a']++;
16        }
17      
18        // Initialize maximum possible copies to a large number
19        int maxCopies = Integer.MAX_VALUE;
20      
21        // For each character in the alphabet
22        for (int i = 0; i < 26; i++) {
23            // If this character is required in the target string
24            if (targetCharCount[i] > 0) {
25                // Calculate how many copies of target can be formed with this character
26                // Update the minimum (bottleneck character determines max copies)
27                maxCopies = Math.min(maxCopies, sourceCharCount[i] / targetCharCount[i]);
28            }
29        }
30      
31        return maxCopies;
32    }
33}
34
1class Solution {
2public:
3    int rearrangeCharacters(string s, string target) {
4        // Array to store frequency of each character in string s
5        int sourceFreq[26]{};
6        // Array to store frequency of each character in target string
7        int targetFreq[26]{};
8      
9        // Count frequency of each character in source string s
10        for (char& c : s) {
11            ++sourceFreq[c - 'a'];
12        }
13      
14        // Count frequency of each character in target string
15        for (char& c : target) {
16            ++targetFreq[c - 'a'];
17        }
18      
19        // Initialize result with a large value (maximum possible copies)
20        int maxCopies = 100;
21      
22        // For each character in the alphabet
23        for (int i = 0; i < 26; ++i) {
24            // If this character exists in target string
25            if (targetFreq[i] > 0) {
26                // Calculate how many copies of target can be formed using this character
27                // Update the minimum (bottleneck character determines max copies)
28                maxCopies = min(maxCopies, sourceFreq[i] / targetFreq[i]);
29            }
30        }
31      
32        return maxCopies;
33    }
34};
35
1/**
2 * Calculates the maximum number of times the target string can be formed
3 * using characters from the source string s
4 * @param s - The source string containing available characters
5 * @param target - The target string to be formed
6 * @returns The maximum number of times target can be formed
7 */
8function rearrangeCharacters(s: string, target: string): number {
9    // Helper function to convert character to array index (0-25 for a-z)
10    const charToIndex = (char: string): number => char.charCodeAt(0) - 97;
11  
12    // Array to store frequency of each character in source string
13    const sourceCharCount: number[] = new Array(26).fill(0);
14  
15    // Array to store frequency of each character in target string
16    const targetCharCount: number[] = new Array(26).fill(0);
17  
18    // Count frequency of each character in source string
19    for (const char of s) {
20        sourceCharCount[charToIndex(char)]++;
21    }
22  
23    // Count frequency of each character in target string
24    for (const char of target) {
25        targetCharCount[charToIndex(char)]++;
26    }
27  
28    // Initialize result with a large number (upper bound)
29    let maxCopies: number = 100;
30  
31    // Check each character that appears in target
32    for (let i = 0; i < 26; i++) {
33        if (targetCharCount[i] > 0) {
34            // Calculate how many times we can form target based on this character
35            const possibleCopies: number = Math.floor(sourceCharCount[i] / targetCharCount[i]);
36            // Update minimum (bottleneck determines the result)
37            maxCopies = Math.min(maxCopies, possibleCopies);
38        }
39    }
40  
41    return maxCopies;
42}
43

Time and Space Complexity

The time complexity is O(n + m), where n is the length of string s and m is the length of string target. This is because:

  • Creating Counter(s) requires iterating through all n characters of string s, taking O(n) time
  • Creating Counter(target) requires iterating through all m characters of string target, taking O(m) time
  • The min() operation iterates through all unique characters in target, which is at most m characters, taking O(m) time
  • Therefore, the total time complexity is O(n) + O(m) + O(m) = O(n + m)

The space complexity is O(|Σ|), where |Σ| represents the size of the character set. In this problem, since we're dealing with lowercase English letters, |Σ| = 26. This is because:

  • Counter(s) stores at most 26 unique lowercase letters and their frequencies
  • Counter(target) stores at most 26 unique lowercase letters and their frequencies
  • Both counters are bounded by the alphabet size rather than the input size
  • Therefore, the space complexity is O(26) + O(26) = O(|Σ|)

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

Common Pitfalls

1. Not Handling Missing Characters Properly

A common mistake is forgetting that if a character in target doesn't exist in s at all, the function should return 0 immediately. While Python's Counter handles this gracefully by returning 0 for missing keys, manually implementing with regular dictionaries can cause KeyError.

Incorrect approach:

def rearrangeCharacters(self, s: str, target: str) -> int:
    char_count = {}
    for char in s:
        char_count[char] = char_count.get(char, 0) + 1
  
    min_copies = float('inf')
    for char in target:
        # This will cause KeyError if char not in char_count
        min_copies = min(min_copies, char_count[char])  # Wrong!
    return min_copies

Solution: Always check if the character exists or use .get() with a default value:

for char in target:
    min_copies = min(min_copies, char_count.get(char, 0))

2. Forgetting to Account for Character Frequency in Target

Another pitfall is only checking if characters exist but not considering how many times each character appears in target. For example, if target = "aaa", you need 3 'a's for each copy, not just 1.

Incorrect approach:

def rearrangeCharacters(self, s: str, target: str) -> int:
    source_count = Counter(s)
    # Wrong: only checking unique characters in target
    return min(source_count[char] for char in set(target))

Solution: Count the frequency of each character in target and divide appropriately:

target_count = Counter(target)
return min(source_count[char] // count for char, count in target_count.items())

3. Using Regular Division Instead of Integer Division

Using / instead of // will return a float, which might seem correct but can lead to issues when the result should be an integer count of complete copies.

Incorrect approach:

# This returns a float
max_copies = min(source_count[char] / count for char, count in target_count.items())
return int(max_copies)  # Converting at the end can introduce rounding errors

Solution: Use integer division // from the start to ensure clean integer arithmetic:

max_copies = min(source_count[char] // count for char, count in target_count.items())

4. Edge Case: Empty Target String

If target is empty, the generator expression will have no items to iterate over, causing min() to raise a ValueError.

Solution: Add a guard clause:

def rearrangeCharacters(self, s: str, target: str) -> int:
    if not target:
        return 0  # or handle as per problem requirements
  
    source_count = Counter(s)
    target_count = Counter(target)
    return min(source_count[char] // count for char, count in target_count.items())
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which of the following uses divide and conquer strategy?


Recommended Readings

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

Load More