Want Help Cracking FAANG?

(Then click this)

×
Back to Question Bank

1657. Determine if Two Strings Are Close - Leetcode Solution

Problem Description

You are given two strings, word1 and word2. Your task is to determine if these two strings are "close" under the following operations:

  • You can swap any two existing characters in a string (i.e., any permutation of the string is allowed).
  • You can transform every occurrence of one existing character into another existing character, and do the same with the other character (i.e., swap all occurrences of two characters).

Two strings are considered "close" if you can make them equal using any number of these operations. The constraints are:

  • Each operation can be performed any number of times.
  • You cannot introduce new characters that do not already exist in the string.
  • You cannot remove characters.

Return true if word1 and word2 are close, and false otherwise.

Thought Process

At first glance, the problem seems similar to checking if two strings are anagrams. However, the allowed operations let us do more than just rearrange characters: we can also swap all instances of one letter with another, which changes the distribution of letters.

The brute-force approach would be to generate all possible strings from word1 using the allowed operations and see if word2 can be obtained. But this is computationally infeasible for large strings.

The key insight is to focus on the properties that are preserved by the allowed operations:

  • We can rearrange letters (so order doesn't matter).
  • We can swap letters, so the multiset of character frequencies can be rearranged among the existing characters.
  • We cannot introduce new characters or remove existing ones, so the set of unique characters must be the same.

Thus, the problem reduces to checking two things:

  1. The set of unique characters in both strings must be identical.
  2. The frequency counts of the characters (as multisets) must match when sorted.

Solution Approach

To solve the problem efficiently, we follow these steps:

  1. Count the frequency of each character in both word1 and word2. This can be done using a hash map or array (since only lowercase English letters are used).
  2. Compare the sets of unique characters in both words. If they differ, return false.
  3. Compare the sorted lists of frequencies for both words. If they match, return true; otherwise, return false.

Why does this work? Because the allowed operations let us permute the frequencies among the existing characters, but not introduce or remove characters. So, if both the sets of unique characters and the sorted frequency lists match, the transformation is possible.

Example Walkthrough

Let's walk through an example with word1 = "abbzzca" and word2 = "babzcaz".

  1. Count character frequencies:
    word1: a:2, b:2, c:1, z:2
    word2: b:2, a:2, z:2, c:1
  2. Unique character sets:
    Both have {'a', 'b', 'c', 'z'}.
  3. Sorted frequencies:
    Both are [1, 2, 2, 2].
  4. Conclusion: Both conditions are met, so return true.

For a negative example, word1 = "aabbcc" and word2 = "aabbc":

  • Unique sets: {'a', 'b', 'c'} vs {'a', 'b', 'c'}
  • Frequencies: [2,2,2] vs [2,2,1]
  • Frequencies do not match, so return false.

Time and Space Complexity

Brute-force approach: Generating all possible transformations is factorial in string length and infeasible for large inputs.

Optimized approach:

  • Time Complexity: O(N), where N is the length of the strings. We scan each string once to count frequencies, and sorting the frequency arrays is O(26*log26), which is O(1) since there are at most 26 letters.
  • Space Complexity: O(1), since we use fixed-size arrays or hash maps for the 26 lowercase letters.

The solution is efficient and scalable for long strings.

Summary

The "Determine if Two Strings Are Close" problem tests your understanding of string manipulation under specific operations. The key insight is that the allowed operations preserve the set of unique characters and allow permutation of their frequencies. By checking these two properties, we can efficiently determine if one string can be transformed into the other, making the solution both elegant and optimal.

Code Implementation

from collections import Counter

class Solution:
    def closeStrings(self, word1: str, word2: str) -> bool:
        count1 = Counter(word1)
        count2 = Counter(word2)
        # Check if unique character sets are equal
        if set(count1.keys()) != set(count2.keys()):
            return False
        # Check if sorted frequencies are equal
        return sorted(count1.values()) == sorted(count2.values())
      
#include <string>
#include <vector>
#include <algorithm>
#include <unordered_set>
using namespace std;

class Solution {
public:
    bool closeStrings(string word1, string word2) {
        vector<int> freq1(26, 0), freq2(26, 0);
        unordered_set<char> set1, set2;
        for (char c : word1) {
            freq1[c - 'a']++;
            set1.insert(c);
        }
        for (char c : word2) {
            freq2[c - 'a']++;
            set2.insert(c);
        }
        if (set1 != set2) return false;
        sort(freq1.begin(), freq1.end());
        sort(freq2.begin(), freq2.end());
        return freq1 == freq2;
    }
};
      
import java.util.*;

class Solution {
    public boolean closeStrings(String word1, String word2) {
        int[] freq1 = new int[26];
        int[] freq2 = new int[26];
        Set<Character> set1 = new HashSet<>();
        Set<Character> set2 = new HashSet<>();
        for (char c : word1.toCharArray()) {
            freq1[c - 'a']++;
            set1.add(c);
        }
        for (char c : word2.toCharArray()) {
            freq2[c - 'a']++;
            set2.add(c);
        }
        if (!set1.equals(set2)) return false;
        Arrays.sort(freq1);
        Arrays.sort(freq2);
        return Arrays.equals(freq1, freq2);
    }
}
      
var closeStrings = function(word1, word2) {
    if (word1.length !== word2.length) return false;
    const freq1 = Array(26).fill(0), freq2 = Array(26).fill(0);
    const set1 = new Set(), set2 = new Set();
    for (const c of word1) {
        freq1[c.charCodeAt(0) - 97]++;
        set1.add(c);
    }
    for (const c of word2) {
        freq2[c.charCodeAt(0) - 97]++;
        set2.add(c);
    }
    if (set1.size !== set2.size || ![...set1].every(x => set2.has(x))) return false;
    freq1.sort((a, b) => a - b);
    freq2.sort((a, b) => a - b);
    for (let i = 0; i < 26; i++) {
        if (freq1[i] !== freq2[i]) return false;
    }
    return true;
};