Minimum Number Of Steps To Make Two Strings Anagram In C++ - C++ Programming Tutorial
C++ Course / Strings / Minimum Number Of Steps To Make Two Strings Anagram In C++

Minimum Number Of Steps To Make Two Strings Anagram In C++

BLUF: Mastering Minimum Number Of Steps To Make Two Strings Anagram In C++ is a critical step in becoming a proficient C++ developer. This lesson provides a deep dive into the syntax, performance considerations, and real-world applications of this concept.
Key Performance Insight: Minimum Number Of Steps To Make Two Strings Anagram In C++

C++ is renowned for its efficiency. Learn how Minimum Number Of Steps To Make Two Strings Anagram In C++ enables low-level control and high-performance computing in the tutorial below.

Introduction:

An anagram is a term created by rearranging the letters of another word or phrase, usually using each original letter once. For example, "listen" and "silent" are examples of anagrams. When dealing with converting two strings into an anagram, the goal is to determine the minimum number of operations required to align the character frequencies of both strings.

Approach 1: Simple Approach

Implementation:

Example

#include <iostream>
#include <string>
#include <vector>
#include <cmath>
int minStepsToMakeAnagram(const std::string &s1, const std::string &s2) {
    // Frequency arrays for each character in the alphabet
    std::vector<int> freq1(26, 0);
    std::vector<int> freq2(26, 0);
    // Count frequency of each character in s1
    for (char c : s1) {
        freq1[c - 'a']++;
    }
    // Count frequency of each character in s2
    for (char c : s2) {
        freq2[c - 'a']++;
    }
    // Calculate the number of changes required
    int steps = 0;
    for (int i = 0; i < 26; i++) {
        steps += std::abs(freq1[i] - freq2[i]);
    }
    // Since changes in one string can balance the other, we divide by 2
    return steps / 2;
}
int main() {
    std::string s1 = "listen";
    std::string s2 = "silent";
    std::cout << "Minimum steps to make the two strings anagram: " << minStepsToMakeAnagram(s1, s2) << std::endl;
    return 0;
}

Output:

Output

Minimum steps to make the two strings anagram: 0

Explanation:

  • Step 1: Frequency Count First, we need to understand how frequently each character appears in both strings. To do this, we create two arrays (or lists) where each element corresponds to a character in the alphabet . For simplicity, we'll assume that the strings only contain lowercase English letters, so each array will have 26 elements, one for each letter from 'a' to 'z'.
  • Step 2: Populate Frequency Arrays Next, we go through each character in the first string and update its corresponding position in the first frequency array. For example, if the first string contains the character 'a', we increase the count at the position corresponding to 'a' in the first array. We repeat this process for the second string and its corresponding frequency array.
  • Step 3: Calculate Differences After populating the frequency arrays, we need to find out how many characters are different between the two strings. For each character (from 'a' to 'z'), we calculate the difference in their frequencies between the two arrays. This tells us how many characters of each type we need to add or remove to make the frequencies match.
  • Step 4: Sum of Differences Once we have the differences for all characters, we add up these differences to get the total number of character changes needed. However, since changes in one string can often balance changes in the other string, the actual number of steps required is half of this total difference.
  • Complexity analysis:

Time Complexity:

The time complexity of this algorithm is O(n), with n representing the longer of the two strings' lengths.

Calculating Occurrences: We cycle through every character in both strings to fill up the frequency arrays. This process requires O(n) time for each string, leading to an overall time complexity of O(n).

Computing Variances: Next, we loop through the predetermined length frequency arrays (with a size of 26) to determine the variances and aggregate them. This process operates in O(1) time due to the fixed size of the array, which remains unchanged regardless of the input string's length.

Given that both the tallying and variance computations are linear processes in relation to the string lengths, the total time complexity persists as O(n).

Space Complexity:

The space complexity of this solution is O(1).

Frequency Arrays: Two arrays with a fixed size of 26 are employed to maintain the character frequency statistics within the given strings. This approach ensures that the space complexity remains constant at O(1), as the size of these arrays is independent of the input length and consistently set at 26 for lowercase letters in English.

Other Variables: The extra memory allocated for storing variables such as the overall steps remains consistent and does not increase with the size of the input.

Therefore, the algorithm utilizes a consistent quantity of additional memory, leading to a space complexity of O(1).

Approach 2: Using Sorting

Employing sorting as a strategy to address the issue of transforming two strings into anagrams follows a simple method where we utilize the arranged sequence of characters to effortlessly pinpoint discrepancies between the two strings. Anagrams refer to strings that are capable of being rearranged to create each other, indicating that after sorting, two anagram strings will match perfectly.

Implementation:

Example

#include <iostream>
#include <algorithm>
#include <string>
int minStepsToMakeAnagramUsingSorting(const std::string &s1, const std::string &s2) {
    // Sort both strings
    std::string sorted_s1 = s1;
    std::string sorted_s2 = s2;
    std::sort(sorted_s1.begin(), sorted_s1.end());
    std::sort(sorted_s2.begin(), sorted_s2.end());
    // Compare sorted strings to find differences
    int steps = 0;
    int i = 0, j = 0;
    while (i < sorted_s1.size() && j < sorted_s2.size()) {
        if (sorted_s1[i] != sorted_s2[j]) {
            steps++;
            // Move the pointer in the string that has the smaller character
            if (sorted_s1[i] < sorted_s2[j]) {
                i++;
            } else {
                j++;
            }
        } else {
            i++;
            j++;
        }
    }
    // Add remaining characters (if any)
    steps += (sorted_s1.size() - i) + (sorted_s2.size() - j);
    return steps;
}
int main() {
    std::string s1 = "listen";
    std::string s2 = "silentz";
    std::cout << "Minimum steps to make the two strings anagram (using sorting): "
              << minStepsToMakeAnagramUsingSorting(s1, s2) << std::endl;
    return 0;
}

Output:

Output

Minimum steps to make the two strings anagram (using sorting): 1

Explanation:

  1. Step 1: Input Strings:

You initiate with two input strings, s1 and s2.

  1. Next Step: Arranging:

Arrangement Procedure: Each of the strings (s1 and s2) has been organized in a sorted manner. This sorting action adjusts the characters within each string so that characters having identical frequencies are positioned in the same sequence.

Why Sort: Sorting simplifies the comparison process because two strings that are anagrams of each other will be identical after sorting.

  1. Step 3: Comparison:

When strings are sorted, you can iterate through them character by character to identify the variance in characters between the two strings.

Analyzing Discrepancies: Examining variances for every index in the ordered strings:

If the characters at the present positions differ, it signifies a required modification to align the strings (anagram).

Increase a counter (steps) whenever there is a variance in characters.

Proceed to the subsequent character in the string if the character is alphabetically lower to ensure a synchronized evaluation.

  1. Step 4: Managing Remaining Characters:

Comparison Conclusion: Following the evaluation of characters in both strings:

  1. Step 5: The outcome:

The total steps required represent the minimum quantity of alterations essential to convert one string into an anagram of the other.

Complexity analysis:

Time Complexity

Arranging: The primary task involved in this method is organizing both strings. The process of sorting a string with a length of n generally requires a time complexity of O(nlogn) when utilizing effective sorting techniques such as Quicksort or Mergesort.

Upon sorting, the comparison of the two arranged strings requires a singular traversal through each string, resulting in a time complexity of O(n), with n representing the longer string's length.

Hence, the total time complexity is O(nlogn), primarily influenced by the sorting process.

Space Complexity

Sorting operations usually involve extra memory that scales with the size of the string undergoing sorting. When sorting is performed directly on the input data (such as with Quicksort which has a space complexity of O(1)), the space required remains constant at O(1). Nevertheless, certain sorting methods might demand additional space of O(n) in the most unfavorable scenarios.

Additional Space: Apart from the input strings, the algorithm uses additional space for variables such as counters and iterators, which is O(1).

Thus, the spatial complexity for this method usually amounts to O(n) at its peak, factoring in the space allocated for sorting and additional storage requirements.

Approach 3: Using a Hash Map

Utilizing a hash map to address the issue of transforming two strings into anagrams necessitates tallying the occurrence of each character in both strings and subsequently contrasting these occurrences. Anagrams, as per their definition, encompass identical characters with identical frequencies, albeit arranged differently. This strategy makes use of hash maps (or associative arrays) to effectively track character occurrences and ascertain the minimal modifications needed for anagram equivalence.

Implementation:

Example

#include <iostream>
#include <unordered_map>
#include <string>
int minStepsToMakeAnagramUsingHashMap(const std::string &s1, const std::string &s2) {
    // Hash maps to count character frequencies
    std::unordered_map<char, int> freq1, freq2;
    // Count frequencies in s1
    for (char c : s1) {
        freq1[c]++;
    }
    // Count frequencies in s2
    for (char c : s2) {
        freq2[c]++;
    }
    // Calculate differences
    int steps = 0;
    for (char c = 'a'; c <= 'z'; ++c) {
        steps += std::abs(freq1[c] - freq2[c]);
    }
    return steps;
}
int main() {
    std::string s1 = "listen";
    std::string s2 = "silentz";
    std::cout << "Minimum steps to make the two strings anagram (using hash map): "
              << minStepsToMakeAnagramUsingHashMap(s1, s2) << std::endl;
    
    return 0;
}

Output:

Output

Minimum steps to make the two strings anagram (using hash map): 1

Explanation:

  1. Step 1: Initialization:

Step 2: Calculating Occurrences:

Two unordered_map instances (freq1 and freq2) are created and initialized. These data structures are employed to keep track of how often each character appears in the strings s1 and s2, correspondingly.

Iterate over Strings: Traverse through every character within string s1. Increment the frequency count of each character in freq1 during this process.

Similarly, iterate over each character within string s2 and increase its frequency count in freq2.

Why Utilize Maps: Employing maps enables effective tallying with a time complexity of O(n) for every string, where n represents the string's length. This efficiency is due to the fact that accessing and modifying elements in an unordered map generally takes O(1) on average.

  1. Step 3: Determining Variances:

Iterate Through Characters: Cycle through all potential characters (usually from 'a' to 'z').

Calculate the absolute variance between the occurrences of each character in freq1 and freq2.

Sum the absolute variances to determine the total steps necessary for equalizing the occurrences in both s1 and s2.

  1. Phase 4: Outcome:

The end result of the sum (sequences) indicates the least amount of modifications required to convert s1 into a rearranged version of s2.

Complexity analysis:

Time Complexity:

Counting Frequencies:

Updating the hash maps (freq1 and freq2) by iterating through every character in strings s1 and s2 has a time complexity of O(n), where n represents the longer string's length among the two given strings.

Calculating Differences:

Traversing the hash maps (usually containing around 26 entries for lowercase English letters) and computing the absolute variances operates with a time complexity of O(1) as the count of distinct characters remains constant.

Overall Time Complexity:

Consequently, the total time complexity is O(n), primarily driven by the process of traversing the strings to calculate the frequencies of characters.

Space Complexity:

Hash Maps:

Two hash maps, namely freq1 and freq2, are employed to record the frequencies of characters in strings s1 and s2. The spatial complexity for each hash map equals O(c), where c represents the cardinality of the character set (commonly 26 for lowercase letters in English).

Additional Space:

Besides hash maps, extra memory is allocated for elements such as iterators and counters, with a constant time complexity of O(1).

Overall Space Complexity:

Therefore, the total space efficiency is O(c), with c representing a fixed and usually minimal value (26 for lowercase letters in English).

Approach 4: Using Two-Pointer Technique

Employing the two-pointer strategy to address the challenge of transforming two strings into anagrams requires a methodical tactic that employs pointers to concurrently examine characters in both strings. This technique proves highly efficient when paired with sorting or frequency analysis, offering a solution with a linear time complexity to distinguish discrepancies between the two strings.

Implementation:

Example

#include <iostream>
#include <algorithm>
#include <string>
int minStepsToMakeAnagramUsingTwoPointer(const std::string &s1, const std::string &s2) {
    // Sort both strings
    std::string sorted_s1 = s1;
    std::string sorted_s2 = s2;
    std::sort(sorted_s1.begin(), sorted_s1.end());
    std::sort(sorted_s2.begin(), sorted_s2.end());
    // Two pointers to traverse both sorted strings
    int steps = 0;
    int i = 0, j = 0;
    while (i < sorted_s1.size() && j < sorted_s2.size()) {
        if (sorted_s1[i] != sorted_s2[j]) {
            steps++;
            // Move the pointer in the string that has the smaller character
            if (sorted_s1[i] < sorted_s2[j]) {
                i++;
            } else {
                j++;
            }
        } else {
            i++;
            j++;
        }
    }
    // Add remaining characters (if any)
    steps += (sorted_s1.size() - i) + (sorted_s2.size() - j);
    return steps;
}
int main() {
    std::string s1 = "listen";
    std::string s2 = "silentz";
    std::cout << "Minimum steps to make the two strings anagram (using two-pointer technique): "
              << minStepsToMakeAnagramUsingTwoPointer(s1, s2) << std::endl;
    return 0;
}

Output:

Output

Minimum steps to make the two strings anagram (using two-pointer technique): 1

Explanation:

  • Step 1: Input Strings: You start with two input strings, s1 and s2.
  • Step 2: Sorting (Optional): Sorting Process: Both strings (s1 and s2) are optionally sorted. Sorting rearranges the characters in each string such that characters with the same frequency appear in the same order. Why Sort: Sorting simplifies the comparison process because two strings that are anagrams of each other will be identical after sorting.
  • Step 3: Two-Pointer Strategy: Initialization: Initialize two pointers (i and j) to traverse through sorteds1 and sorteds2, respectively. These pointers start at the beginning of each sorted string. Comparison: Iterate through both sorted strings simultaneously using the two pointers: Compare characters pointed to by i in sorteds1 and j in sorteds2. If the characters are different, increment a counter (steps) indicating the need for a change to make them match (an anagram). Move the pointer thacpp tutorials to the smaller character lexicographically to maintain synchronization and continue comparing.
  • Step 4: Handling Remaining Characters: End of Comparison: After comparing characters in both strings: Any remaining characters in either string that were not compared (due to differing lengths) are also counted as necessary changes (steps).
  • Step 5: Result: The final value of steps gives the minimum number of character modifications needed to transform one string into an anagram of the other.
  • Example:

Consider the strings "listen" and "silentz":

Sorting (Optional):

After arranging:

  • "listen" transforms into "eilnst"
  • "silentz" changes to "eilnstz"

Two-Pointer Comparison:

Utilize two pointers to compare characters at each position in the given scenario.

Iterate through both sorted strings simultaneously:

  • Compare 'e' and 'e' (match), move both pointers.
  • Compare 'i' and 'i' (match), move both pointers.
  • Compare 'l' and 'l' (match), move both pointers.
  • Compare 'n' and 'n' (match), move both pointers.
  • Compare 's' and 's' (match), move both pointers.
  • Compare 't' and 'z' (difference detected), increment steps counter.

Result:

The count of steps will increase by 1 due to the difference between 't' and 'z', showing that one modification (adding 'z') is required to transform the strings into anagrams.

Complexity analysis:

Time Complexity:

Sorting (if applied):

If the sorting process is initiated initially, the time complexity is primarily influenced by the sorting operation, which is O(nlogn), with n representing the longer string's length between s1 and s2.

Two-Pointer Comparison:

Upon sorting or conducting a direct comparison, utilizing two pointers entails traversing through both strings just once, resulting in a time complexity of O(n), where n represents the length of the longer string among s1 and s2.

Overall Time Complexity:

Therefore, the total time complexity is O(nlogn) when sorting is implemented, and O(n) when sorting is excluded. However, in practice, it is usually O(nlogn) due to the inclusion of the sorting process.

Space Complexity:

Sorting (if applied):

Sorting might necessitate extra memory depending on the specific sorting method employed. Usually, the auxiliary space complexity is O(n), where n represents the size of the longer string.

Two-Pointer Technique:

The space efficiency of the two-pointer approach remains constant at O(1) since it solely relies on a limited number of extra variables (such as pointers and counters) that do not increase with the input magnitude.

Overall Space Complexity:

Taking into account the sorting process and the extra storage needed for variables, the space complexity in the worst-case scenario is O(n), where n represents the longer string's length.

Input Required

This code uses input(). Please provide values below:

Logic Practice
Install Logic Practice
Add to home screen for a faster app-like experience