Facebook Pixel

2186. Minimum Number of Steps to Make Two Strings Anagram II

MediumHash TableStringCounting
Leetcode Link

Problem Description

You are given two strings s and t. Your task is to make these two strings anagrams of each other by appending characters.

In each step, you can append any character to either string s or string t. You need to find the minimum number of steps required to make s and t anagrams of each other.

An anagram is a string that contains the exact same characters as another string, but potentially in a different order. For example, "abc" and "bca" are anagrams, as are "aab" and "aba".

The key insight is that for two strings to become anagrams, they must have the same frequency of each character. If string s has more occurrences of a character than string t, you need to add that character to t (and vice versa). The minimum number of steps is the total number of characters you need to add to balance out the character frequencies between the two strings.

For example:

  • If s = "abc" and t = "ab", you need to add one 'c' to t to make them anagrams, requiring 1 step
  • If s = "aaa" and t = "bbb", you need to add three 'b's to s and three 'a's to t to make them anagrams, requiring 6 steps total
Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

To make two strings anagrams, we need to ensure they have identical character frequencies. Let's think about what happens when the frequencies don't match:

  • If string s has 3 'a's and string t has 1 'a', we need to add 2 'a's to t
  • If string s has 1 'b' and string t has 4 'b's, we need to add 3 'b's to s

The key realization is that we can track the frequency difference for each character. We can count the characters in string s, then subtract the counts based on what appears in string t.

After this subtraction:

  • A positive count means s has excess of that character (we need to add it to t)
  • A negative count means t has excess of that character (we need to add it to s)
  • A zero count means the character is already balanced

For example, if after processing we have {'a': 2, 'b': -3, 'c': 0}:

  • We need to add 2 'a's to t (2 steps)
  • We need to add 3 'b's to s (3 steps)
  • Character 'c' is already balanced (0 steps)

The total number of steps is |2| + |-3| + |0| = 5. By taking the absolute value of each frequency difference and summing them up, we get the minimum number of characters we need to append across both strings.

This approach works because each character imbalance needs to be fixed by adding characters, and the absolute value tells us exactly how many additions are needed regardless of which string has the excess.

Solution Approach

The implementation uses a frequency counting approach with Python's Counter data structure:

  1. Count character frequencies in string s: We create a Counter object from string s, which gives us a dictionary-like structure where keys are characters and values are their frequencies. For example, if s = "aab", then cnt = {'a': 2, 'b': 1}.

  2. Subtract frequencies from string t: We iterate through each character in string t and decrement its count in our counter. This effectively calculates the frequency difference between s and t for each character:

    for c in t:
        cnt[c] -= 1

    After this step:

    • Positive values indicate characters that appear more in s than in t
    • Negative values indicate characters that appear more in t than in s
    • Zero values indicate balanced characters
  3. Calculate total steps needed: We sum up the absolute values of all frequency differences:

    return sum(abs(v) for v in cnt.values())

    The absolute value is crucial because:

    • A count of 3 means we need to add 3 of that character to t
    • A count of -2 means we need to add 2 of that character to s
    • Both contribute positively to our total step count

Example walkthrough:

  • Given s = "leetcode" and t = "coats"
  • After counting s: {'l': 1, 'e': 3, 't': 1, 'c': 1, 'o': 1, 'd': 1}
  • After subtracting t: {'l': 1, 'e': 3, 't': 0, 'c': 0, 'o': -1, 'd': 1, 'a': -1, 's': -1}
  • Sum of absolute values: |1| + |3| + |0| + |0| + |-1| + |1| + |-1| + |-1| = 8
  • Result: 8 steps needed

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 small example with s = "abc" and t = "ab".

Step 1: Count frequencies in string s

  • We create a counter from s = "abc"
  • Initial counter: {'a': 1, 'b': 1, 'c': 1}

Step 2: Subtract frequencies from string t

  • Process each character in t = "ab":
    • For 'a': cnt['a'] = 1 - 1 = 0
    • For 'b': cnt['b'] = 1 - 1 = 0
  • Counter after subtraction: {'a': 0, 'b': 0, 'c': 1}

Step 3: Interpret the results

  • 'a': 0 means 'a' is balanced (appears equally in both strings)
  • 'b': 0 means 'b' is balanced (appears equally in both strings)
  • 'c': 1 means 'c' appears 1 more time in s than in t

Step 4: Calculate minimum steps

  • Sum of absolute values: |0| + |0| + |1| = 1
  • We need to add 1 character ('c') to string t

Verification: After adding 'c' to t, we have:

  • s = "abc" with frequencies {'a': 1, 'b': 1, 'c': 1}
  • t = "abc" with frequencies {'a': 1, 'b': 1, 'c': 1}
  • Both strings are now anagrams!

The answer is 1 step.

Solution Implementation

1from collections import Counter
2
3class Solution:
4    def minSteps(self, s: str, t: str) -> int:
5        # Count frequency of each character in string s
6        char_count = Counter(s)
7      
8        # Subtract frequency for each character in string t
9        for char in t:
10            char_count[char] -= 1
11      
12        # Sum up absolute values of all frequency differences
13        # This represents minimum steps needed to balance the strings
14        return sum(abs(frequency) for frequency in char_count.values())
15
1class Solution {
2    public int minSteps(String s, String t) {
3        // Create frequency array for 26 lowercase English letters
4        int[] charFrequency = new int[26];
5      
6        // Count character frequencies in string s (increment)
7        for (char character : s.toCharArray()) {
8            charFrequency[character - 'a']++;
9        }
10      
11        // Subtract character frequencies from string t (decrement)
12        for (char character : t.toCharArray()) {
13            charFrequency[character - 'a']--;
14        }
15      
16        // Calculate total steps needed
17        // Sum of absolute differences represents characters that need to be changed
18        int totalSteps = 0;
19        for (int frequency : charFrequency) {
20            totalSteps += Math.abs(frequency);
21        }
22      
23        return totalSteps;
24    }
25}
26
1class Solution {
2public:
3    int minSteps(string s, string t) {
4        // Create a frequency array to track character count differences
5        // Index represents character (0 for 'a', 1 for 'b', ..., 25 for 'z')
6        vector<int> charFrequency(26, 0);
7      
8        // Count frequency of each character in string s (add to counter)
9        for (const char& ch : s) {
10            charFrequency[ch - 'a']++;
11        }
12      
13        // Subtract frequency of each character in string t (subtract from counter)
14        for (const char& ch : t) {
15            charFrequency[ch - 'a']--;
16        }
17      
18        // Calculate total steps needed
19        // Positive values: characters excess in s that need to be replaced
20        // Negative values: characters excess in t that need to be replaced
21        // Sum of absolute values gives total replacements needed
22        int totalSteps = 0;
23        for (const int& frequency : charFrequency) {
24            totalSteps += abs(frequency);
25        }
26      
27        return totalSteps;
28    }
29};
30
1/**
2 * Calculates the minimum number of steps to make two strings anagrams of each other.
3 * A step is defined as replacing one character with another character.
4 * 
5 * @param s - The first string
6 * @param t - The second string
7 * @returns The total number of character replacements needed
8 */
9function minSteps(s: string, t: string): number {
10    // Initialize frequency array for all ASCII characters (128 possible values)
11    const charFrequency: number[] = new Array(128).fill(0);
12  
13    // Count character frequencies in string s (increment)
14    for (const char of s) {
15        charFrequency[char.charCodeAt(0)]++;
16    }
17  
18    // Subtract character frequencies from string t (decrement)
19    for (const char of t) {
20        charFrequency[char.charCodeAt(0)]--;
21    }
22  
23    // Calculate total steps needed
24    // The absolute value of each frequency represents the number of changes needed
25    let totalSteps: number = 0;
26    for (const frequency of charFrequency) {
27        totalSteps += Math.abs(frequency);
28    }
29  
30    return totalSteps;
31}
32

Time and Space Complexity

Time Complexity: O(n + m) where n is the length of string s and m is the length of string t.

  • Creating a Counter from string s takes O(n) time as it needs to iterate through all characters in s
  • The for loop iterates through all characters in string t, which takes O(m) time
  • The final sum operation iterates through all unique characters in the Counter (at most O(n) items), and computing absolute value for each is O(1)
  • Since the number of unique characters is bounded by the length of the strings, the overall time complexity is O(n + m)

Space Complexity: O(k) where k is the number of unique characters in string s.

  • The Counter object stores at most k unique characters from string s, where k ≤ n
  • In the worst case where all characters in s are unique, the space complexity would be O(n)
  • In practice, if we're dealing with lowercase English letters only, k is bounded by 26, making it O(1) constant space
  • No additional data structures are created that scale with input size

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

Common Pitfalls

1. Misunderstanding the Problem Goal

A common mistake is thinking we need to transform one string into the other, rather than making them anagrams. Remember that anagrams only need the same character frequencies, not the same order.

Incorrect interpretation: Trying to count operations to change s = "abc" to exactly match t = "bca" Correct interpretation: Both strings already have the same character frequencies, so 0 steps are needed

2. Double-Counting Character Differences

Some might incorrectly count both positive and negative differences separately without realizing they represent the same operation from different perspectives.

Pitfall Example:

# INCORRECT: This counts each mismatch twice
def minSteps(self, s: str, t: str) -> int:
    cnt_s = Counter(s)
    cnt_t = Counter(t)
    steps = 0
  
    # Don't do this - it double counts!
    for char in cnt_s:
        if cnt_s[char] > cnt_t[char]:
            steps += cnt_s[char] - cnt_t[char]
    for char in cnt_t:
        if cnt_t[char] > cnt_s[char]:
            steps += cnt_t[char] - cnt_s[char]
  
    return steps  # This returns twice the correct answer

Solution: The provided approach correctly handles this by subtracting one counter from the other and taking absolute values, ensuring each difference is counted exactly once.

3. Forgetting Absolute Values

When calculating the total steps, forgetting to use absolute values will give incorrect results because negative frequencies would reduce the total count.

Pitfall Example:

# INCORRECT: Missing absolute value
return sum(v for v in cnt.values())  # Could return 0 or negative number

Solution: Always use absolute values when summing the frequency differences:

return sum(abs(v) for v in cnt.values())

4. Not Handling Characters Present in Only One String

Python's Counter handles this automatically, but if implementing manually with a regular dictionary, you might encounter KeyError when decrementing counts for characters not in the first string.

Pitfall Example:

# INCORRECT: Will raise KeyError if t has characters not in s
char_count = {}
for c in s:
    char_count[c] = char_count.get(c, 0) + 1

for c in t:
    char_count[c] -= 1  # KeyError if c not in char_count

Solution: Use Counter which handles missing keys gracefully, or use get() method with default value:

for c in t:
    char_count[c] = char_count.get(c, 0) - 1
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

How many times is a tree node visited in a depth first search?


Recommended Readings

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

Load More