Isomorphic Strings Check
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 Solution – 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’.
Using 2 Hash Maps – O(n) Time and O(MAX_CHAR) Space
MAX_CHAR is maximum number of different characters in the two strings. If we talk about lower case alphabet, then its value is 26
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.
#include <iostream>
#include <unordered_map>
#include <string>
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;
}
import java.util.HashMap;
import java.util.Map;
public class Main {
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");
}
}
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")
using System;
using System.Collections.Generic;
class Program {
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");
}
}
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");
Output
True
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.
#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;
}
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");
}
}
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")
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");
}
}
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");
Using 1 Hash Map and 1 Hash Set – 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.
Below is the implementation of the above approach
#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;
}
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");
}
}
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")
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");
}
}
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");
Output
True
This method can also be further optimized using fixed sized arrays of size 256 instead of map and set.