Two strings s1 and s2 are called isomorphic if there is a one-to-one mapping possible for every character of s1 to every character of s2. And all occurrences of every character in 's1' map to the same character in 's2'.
Examples:
Input: s1 = "aab", s2 = "xxy"
Output: True
Explanation: 'a' is mapped to 'x' and 'b' is mapped to 'y'.
Input: s1 = "aab", s2 = "xyz"
Output: False
Explanation: One occurrence of 'a' in s1 has 'x' in s2 and other occurrence of 'a' has 'y'.
[Naive Approach] Check Every Character - O(n^2) Time and O(1) Space
A Simple Solution is to consider every character of 's1' and check if all occurrences of it map to the same character in 's2'.
C++ #include <iostream> #include <string> using namespace std; bool areIsomorphic(string s1, string s2) { // If lengths are different, they can't be isomorphic if (s1.length() != s2.length()) { return false; } int n = s1.length(); // Check every character of s1 for (int i = 0; i < n; i++) { char c1 = s1[i]; char c2 = s2[i]; // Check all occurrences of c1 in s1 // and corresponding occurrences of c2 in s2 for (int j = 0; j < n; j++) { // If we find another occurrence of c1 in s1, // it must match the corresponding character in s2 if (s1[j] == c1 && s2[j] != c2) { return false; } // If we find another occurrence of c2 in s2, // it must match the corresponding character in s1 if (s2[j] == c2 && s1[j] != c1) { return false; } } } return true; } int main() { string s1 = "aab"; string s2 = "xxy"; if (areIsomorphic(s1, s2)) { cout << "True\n"; } else { cout << "False\n"; } return 0; }
Java import java.util.HashMap; import java.util.Map; public class GfG { public static boolean areIsomorphic(String s1, String s2) { // If lengths are different, they can't be // isomorphic if (s1.length() != s2.length()) { return false; } Map<Character, Character> map1 = new HashMap<>(); Map<Character, Character> map2 = new HashMap<>(); for (int i = 0; i < s1.length(); i++) { char c1 = s1.charAt(i); char c2 = s2.charAt(i); // Check mapping for s1 to s2 if (map1.containsKey(c1) && map1.get(c1) != c2) { return false; } map1.put(c1, c2); // Check mapping for s2 to s1 if (map2.containsKey(c2) && map2.get(c2) != c1) { return false; } map2.put(c2, c1); } return true; } public static void main(String[] args) { String s1 = "aab"; String s2 = "xxy"; if (areIsomorphic(s1, s2)) { System.out.println("True"); } else { System.out.println("False"); } } }
Python # Python implementation def are_isomorphic(s1, s2): # If lengths are different, they can't be isomorphic if len(s1) != len(s2): return False map1, map2 = {}, {} for c1, c2 in zip(s1, s2): # Check mapping for s1 to s2 if c1 in map1: if map1[c1] != c2: return False else: map1[c1] = c2 # Check mapping for s2 to s1 if c2 in map2: if map2[c2] != c1: return False else: map2[c2] = c1 return True s1 = "aab" s2 = "xxy" if are_isomorphic(s1, s2): print("True") else: print("False")
C# using System; using System.Collections.Generic; class GfG { public static bool AreIsomorphic(string s1, string s2) { // If lengths are different, they can't be // isomorphic if (s1.Length != s2.Length) { return false; } Dictionary<char, char> map1 = new Dictionary<char, char>(); Dictionary<char, char> map2 = new Dictionary<char, char>(); for (int i = 0; i < s1.Length; i++) { char c1 = s1[i]; char c2 = s2[i]; // Check mapping for s1 to s2 if (map1.ContainsKey(c1) && map1[c1] != c2) { return false; } map1[c1] = c2; // Check mapping for s2 to s1 if (map2.ContainsKey(c2) && map2[c2] != c1) { return false; } map2[c2] = c1; } return true; } static void Main() { string s1 = "aab"; string s2 = "xxy"; if (AreIsomorphic(s1, s2)) { Console.WriteLine("True"); } else { Console.WriteLine("False"); } } }
JavaScript function areIsomorphic(s1, s2) { // If lengths are different, they can't be isomorphic if (s1.length !== s2.length) { return false; } const map1 = {}; const map2 = {}; for (let i = 0; i < s1.length; i++) { const c1 = s1[i]; const c2 = s2[i]; // Check mapping for s1 to s2 if (map1[c1] !== undefined && map1[c1] !== c2) { return false; } map1[c1] = c2; // Check mapping for s2 to s1 if (map2[c2] !== undefined && map2[c2] !== c1) { return false; } map2[c2] = c1; } return true; } const s1 = "aab"; const s2 = "xxy"; if (areIsomorphic(s1, s2)) { console.log("True"); } else { console.log("False"); }
[Approach 1] Using Hash Maps - O(n) time and O(MAX_CHAR) space
The idea is based on the fact that all occurrences of two characters should be at same index. We mainly store the first index of every character and for remaining occurrences, we check if they appear at same first index too.
We mainly use two maps m1 and m2 to store characters as keys and their first indexes as values.
- If the lengths of s1 and s2 are not same, return false.
- Do the following for every character in s1 and s2.
- If this character is seen first time in s1, then store is index in map m1.
- If this character is seen first time in s2, then store is index in map m2.
- If indexes in map for both the characters do not match, return false.
C++ #include <iostream> #include <string> #include <unordered_map> using namespace std; bool isIsomorphic(const string &s1, const string &s2) { if (s1.length() != s2.length()) return false; unordered_map<char, int> m1, m2; for (int i = 0; i < s1.length(); ++i) { // If character not seen before, store its // first occurrence index if (m1.find(s1[i]) == m1.end()) { m1[s1[i]] = i; } if (m2.find(s2[i]) == m2.end()) { m2[s2[i]] = i; } // Check if the first occurrence indices match if (m1[s1[i]] != m2[s2[i]]) { return false; } } return true; } int main() { cout << (isIsomorphic("aab", "xxy") ? "True" : "False"); return 0; }
Java import java.util.HashMap; import java.util.Map; public class GfG{ public static boolean isIsomorphic(String s1, String s2) { if (s1.length() != s2.length()) return false; Map<Character, Integer> m1 = new HashMap<>(); Map<Character, Integer> m2 = new HashMap<>(); for (int i = 0; i < s1.length(); ++i) { // If character not seen before, store its // first occurrence index if (!m1.containsKey(s1.charAt(i))) { m1.put(s1.charAt(i), i); } if (!m2.containsKey(s2.charAt(i))) { m2.put(s2.charAt(i), i); } // Check if the first occurrence indices match if (!m1.get(s1.charAt(i)) .equals(m2.get(s2.charAt(i)))) { return false; } } return true; } public static void main(String[] args) { System.out.println( isIsomorphic("aab", "xxy") ? "True" : "False"); } }
Python def is_isomorphic(s1, s2): if len(s1) != len(s2): return False m1, m2 = {}, {} for i in range(len(s1)): # If character not seen before, store # its first occurrence index if s1[i] not in m1: m1[s1[i]] = i if s2[i] not in m2: m2[s2[i]] = i # Check if the first occurrence indices match if m1[s1[i]] != m2[s2[i]]: return False return True print("True" if is_isomorphic("aab", "xxy") else "False")
C# using System; using System.Collections.Generic; class GfG { static bool IsIsomorphic(string s1, string s2) { if (s1.Length != s2.Length) return false; Dictionary<char, int> m1 = new Dictionary<char, int>(); Dictionary<char, int> m2 = new Dictionary<char, int>(); for (int i = 0; i < s1.Length; ++i) { // If character not seen before, store its first // occurrence index if (!m1.ContainsKey(s1[i])) { m1[s1[i]] = i; } if (!m2.ContainsKey(s2[i])) { m2[s2[i]] = i; } // Check if the first occurrence indices match if (m1[s1[i]] != m2[s2[i]]) { return false; } } return true; } static void Main() { Console.WriteLine( IsIsomorphic("aab", "xxy") ? "True" : "False"); } }
JavaScript function isIsomorphic(s1, s2) { if (s1.length !== s2.length) return false; const m1 = {}, m2 = {}; for (let i = 0; i < s1.length; i++) { // If character not seen before, store its first // occurrence index if (!(s1[i] in m1)) { m1[s1[i]] = i; } if (!(s2[i] in m2)) { m2[s2[i]] = i; } // Check if the first occurrence indices match if (m1[s1[i]] !== m2[s2[i]]) { return false; } } return true; } console.log(isIsomorphic("aab", "xxy") ? "True" : "False");
[Approach 2] Further Optimization using a Fixed Array of size 256
We can avoid the language specific hash function computation and use our own fixed sized array using the fact that the alphabet size 256.
C++ #include <iostream> #include <vector> #include <string> using namespace std; const int MAX_CHAR = 256; bool isIsomorphic(const string &s1, const string &s2) { if (s1.length() != s2.length()) return false; // To store first indices of s1's characters vector<int> m1(MAX_CHAR, -1); // To store first indices of s2's characters vector<int> m2(MAX_CHAR, -1); for (int i = 0; i < s1.length(); ++i) { // If character's first occurrence hasn't been // recorded, store the index if (m1[s1[i]] == -1) { m1[s1[i]] = i; } if (m2[s2[i]] == -1) { m2[s2[i]] = i; } // Check if the first occurrence indices match if (m1[s1[i]] != m2[s2[i]]) { return false; } } return true; } int main() { cout << (isIsomorphic("aab", "xxy") ? "True" : "False") << endl; return 0; }
Java import java.util.HashMap; import java.util.Map; public class IsomorphicStrings { public static boolean isIsomorphic(String s1, String s2) { if (s1.length() != s2.length()) return false; // To store first indices of s1's characters Map<Character, Integer> m1 = new HashMap<>(); // To store first indices of s2's characters Map<Character, Integer> m2 = new HashMap<>(); for (int i = 0; i < s1.length(); i++) { // If character's first occurrence hasn't been // recorded, store the index m1.putIfAbsent(s1.charAt(i), i); m2.putIfAbsent(s2.charAt(i), i); // Check if the first occurrence indices match if (!m1.get(s1.charAt(i)).equals(m2.get(s2.charAt(i)))) { return false; } } return true; } public static void main(String[] args) { System.out.println(isIsomorphic("aab", "xxy") ? "True" : "False"); } }
Python def is_isomorphic(s1, s2): if len(s1) != len(s2): return False # To store first indices of s1's characters m1 = {} # To store first indices of s2's characters m2 = {} for i in range(len(s1)): # If character's first occurrence hasn't been # recorded, store the index if s1[i] not in m1: m1[s1[i]] = i if s2[i] not in m2: m2[s2[i]] = i # Check if the first occurrence indices match if m1[s1[i]] != m2[s2[i]]: return False return True print("True" if is_isomorphic("aab", "xxy") else "False")
C# using System; using System.Collections.Generic; class Program { static bool IsIsomorphic(string s1, string s2) { if (s1.Length != s2.Length) return false; // To store first indices of s1's characters Dictionary<char, int> m1 = new Dictionary<char, int>(); // To store first indices of s2's characters Dictionary<char, int> m2 = new Dictionary<char, int>(); for (int i = 0; i < s1.Length; i++) { // If character's first occurrence hasn't been // recorded, store the index if (!m1.ContainsKey(s1[i])) m1[s1[i]] = i; if (!m2.ContainsKey(s2[i])) m2[s2[i]] = i; // Check if the first occurrence indices match if (m1[s1[i]] != m2[s2[i]]) { return false; } } return true; } static void Main() { Console.WriteLine(IsIsomorphic("aab", "xxy") ? "True" : "False"); } }
JavaScript function isIsomorphic(s1, s2) { if (s1.length !== s2.length) return false; // To store first indices of s1's characters const m1 = {}; // To store first indices of s2's characters const m2 = {}; for (let i = 0; i < s1.length; i++) { // If character's first occurrence hasn't been // recorded, store the index if (!(s1[i] in m1)) m1[s1[i]] = i; if (!(s2[i] in m2)) m2[s2[i]] = i; // Check if the first occurrence indices match if (m1[s1[i]] !== m2[s2[i]]) { return false; } } return true; } console.log(isIsomorphic("aab", "xxy") ? "True" : "False");
[Approach 3] Using a Set instead of Map for s2- O(n) Time and O(MAX_CHAR) Space
The idea is to store mapping of characters from s1 to s2 in a map and already seen characters of s2 in a set.
Follow the steps to solve the problem:
- Create a hashmap of (char, char) to store the mapping of s1 and s2.
- Now traverse on the string and check whether the current character is present in the Hash map.
- If it is present then the character that is mapped is there at the ith index or not.
- Else If s2[i] is present in the set then s2[i] is already mapped to something else, return false
- Else store mapping of both characters and store s2[i] in the set.
C++ #include <iostream> #include <unordered_map> #include <unordered_set> #include <string> using namespace std; bool isIsomorphic(const string &s1, const string &s2) { if (s1.length() != s2.length()) return false; // character mapping from s1 to s2 unordered_map<char, char> m1; // Already mapped characters in s2 unordered_set<char> set2; for (int i = 0; i < s1.length(); ++i) { char c1 = s1[i], c2 = s2[i]; // If c1 is already mapped if (m1.find(c1) != m1.end()) { // Check if it maps to the current // character in s2 if (m1[c1] != c2) return false; } // First occurrence of c1 else { // Ensure c2 is not already mapped // to another character if (set2.find(c2) != set2.end()) return false; // Create a new mapping and mark c2 as mapped m1[c1] = c2; set2.insert(c2); } } return true; } int main() { cout << (isIsomorphic("aab", "xxy") ? "True" : "False") << endl; return 0; }
Java import java.util.HashMap; import java.util.HashSet; public class Main { public static boolean isIsomorphic(String s1, String s2) { if (s1.length() != s2.length()) return false; // character mapping from s1 to s2 HashMap<Character, Character> m1 = new HashMap<>(); // Already mapped characters in s2 HashSet<Character> set2 = new HashSet<>(); for (int i = 0; i < s1.length(); i++) { char c1 = s1.charAt(i), c2 = s2.charAt(i); // If c1 is already mapped if (m1.containsKey(c1)) { // Check if it maps to the current character in s2 if (m1.get(c1) != c2) return false; } else { // Ensure c2 is not already mapped to another character if (set2.contains(c2)) return false; // Create a new mapping and mark c2 as mapped m1.put(c1, c2); set2.add(c2); } } return true; } public static void main(String[] args) { System.out.println(isIsomorphic("aab", "xxy") ? "True" : "False"); } }
Python def is_isomorphic(s1, s2): if len(s1) != len(s2): return False # character mapping from s1 to s2 m1 = {} # Already mapped characters in s2 set2 = set() for c1, c2 in zip(s1, s2): # If c1 is already mapped if c1 in m1: # Check if it maps to the current character in s2 if m1[c1] != c2: return False else: # Ensure c2 is not already mapped to another character if c2 in set2: return False # Create a new mapping and mark c2 as mapped m1[c1] = c2 set2.add(c2) return True if __name__ == '__main__': print("True" if is_isomorphic("aab", "xxy") else "False")
C# using System; using System.Collections.Generic; class Program { public static bool IsIsomorphic(string s1, string s2) { if (s1.Length != s2.Length) return false; // character mapping from s1 to s2 Dictionary<char, char> m1 = new Dictionary<char, char>(); // Already mapped characters in s2 HashSet<char> set2 = new HashSet<char>(); for (int i = 0; i < s1.Length; i++) { char c1 = s1[i], c2 = s2[i]; // If c1 is already mapped if (m1.ContainsKey(c1)) { // Check if it maps to the current character in s2 if (m1[c1] != c2) return false; } else { // Ensure c2 is not already mapped to another character if (set2.Contains(c2)) return false; // Create a new mapping and mark c2 as mapped m1[c1] = c2; set2.Add(c2); } } return true; } static void Main() { Console.WriteLine(IsIsomorphic("aab", "xxy") ? "True" : "False"); } }
JavaScript function isIsomorphic(s1, s2) { if (s1.length !== s2.length) return false; // character mapping from s1 to s2 const m1 = {}; // Already mapped characters in s2 const set2 = new Set(); for (let i = 0; i < s1.length; i++) { const c1 = s1[i], c2 = s2[i]; // If c1 is already mapped if (m1[c1]) { // Check if it maps to the current character in s2 if (m1[c1] !== c2) return false; } else { // Ensure c2 is not already mapped to another character if (set2.has(c2)) return false; // Create a new mapping and mark c2 as mapped m1[c1] = c2; set2.add(c2); } } return true; } console.log(isIsomorphic("aab", "xxy") ? "True" : "False");
This method can also be further optimized using fixed sized arrays of size 256 instead of map and set.