2186. Minimum Number of Steps to Make Two Strings Anagram II
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"
andt = "ab"
, you need to add one 'c' tot
to make them anagrams, requiring 1 step - If
s = "aaa"
andt = "bbb"
, you need to add three 'b's tos
and three 'a's tot
to make them anagrams, requiring 6 steps total
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 stringt
has 1 'a', we need to add 2 'a's tot
- If string
s
has 1 'b' and stringt
has 4 'b's, we need to add 3 'b's tos
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 tot
) - A negative count means
t
has excess of that character (we need to add it tos
) - 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:
-
Count character frequencies in string
s
: We create aCounter
object from strings
, which gives us a dictionary-like structure where keys are characters and values are their frequencies. For example, ifs = "aab"
, thencnt = {'a': 2, 'b': 1}
. -
Subtract frequencies from string
t
: We iterate through each character in stringt
and decrement its count in our counter. This effectively calculates the frequency difference betweens
andt
for each character:for c in t: cnt[c] -= 1
After this step:
- Positive values indicate characters that appear more in
s
than int
- Negative values indicate characters that appear more in
t
than ins
- Zero values indicate balanced characters
- Positive values indicate characters that appear more in
-
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 tot
- A count of
-2
means we need to add 2 of that character tos
- Both contribute positively to our total step count
- A count of
Example walkthrough:
- Given
s = "leetcode"
andt = "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 EvaluatorExample 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
- For 'a':
- 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 ins
than int
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
takesO(n)
time as it needs to iterate through all characters ins
- The for loop iterates through all characters in string
t
, which takesO(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 isO(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 strings
, wherek ≤ n
- In the worst case where all characters in
s
are unique, the space complexity would beO(n)
- In practice, if we're dealing with lowercase English letters only,
k
is bounded by 26, making itO(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
How many times is a tree node visited in a depth first search?
Recommended Readings
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!