Isomorphic Strings
Different approaches to solve Leetcode 205 in JavaScript
Photo by Mick Haupt on Unsplash
Isomorphic String is a classic problem that challenges us to determine whether two given strings have a one-to-one character mapping. In other words, we need to check if we can replace characters in one string with corresponding characters from the other string while preserving the order.
In this article, we’ll explore an effective solution to solve this problem. We’ll analyze the time and space complexity of our solution, enabling us to understand its efficiency and scalability.
Problem Statement:
Given two strings
s
andt
, determine if they are isomorphic.Two strings
s
andt
are isomorphic if the characters ins
can be replaced to gett
.All occurrences of a character must be replaced with another character while preserving the order of characters. No two characters may map to the same character, but a character may map to itself.
Approach 1: Index Comparison
We iterate through each character in the strings
s
andt
and compare their corresponding indices using theindexOf
method.If the indices of the current characters do not match, we return
false
, indicating that the strings are not isomorphic.If the loop completes without finding any inconsistencies, we return
true
, indicating that the strings are isomorphic.
// ES6 Arrow Function
const isIsomorphic = (s, t) => {
if(s.length !== t.length) return false;
for(let i = 0; i < s.length; i++) {
if(s.indexOf(s[i]) !== t.indexOf(t[i])) {
return false;
}
}
return true;
}
Time Complexity: O(N²), where N is the length of the strings s
and t
.
For each character in s
, the code uses the indexOf
method to search for the next occurrence of the character in both s
and t
. The indexOf
method has a time complexity of O(n) in the worst case, and this search is performed for each character in s
.
Space Complexity: O(1)
It does not use any additional data structures that scale with the input size. It only uses a constant amount of space to store variables and temporary values during the execution of the function.
Approach 2: Character Mapping
The code uses two
Map
objects, to store the mapping between characters ins
andt
.It iterates through each character in the strings and checks if both maps do not contain mappings for the current characters.
If not, it adds the mapping between the characters to both maps.
If the maps already contain mappings, it checks if the existing mappings match the current characters.
If they don’t match, the strings are not isomorphic, and the function returns
false
.If the loop completes without finding any inconsistencies, the strings are considered isomorphic, and the function returns
true
.
// ES6 Arrow Function
const isIsomorphic = (s, t) => {
if(s.length !== t.length) return false;
const sMap = new Map();
const tMap = new Map();
for(let i = 0; i < s.length; i++) {
const charS = s[i];
const charT = t[i];
if(!sMap.has(charS) && !tMap.has(charT)) {
sMap.set(charS, charT);
tMap.set(charT, charS);
} else {
if(sMap.get(charS) !== charT || tMap.get(charT) !== charS) {
return false;
}
}
}
return true;
}
Time Complexity: O(N)
We are iterating through each character in the strings s
and t
once, performing constant-time operations within the loop. Therefore, the time complexity is linear.
Space Complexity: O(1)
The space complexity is determined by the size of the maps sMap
and tMap
. In the worst case, all characters in s
and t
are unique, resulting in a total of 26 entries in each map (assuming lowercase English alphabets). Therefore, the space complexity is O(1), as the maximum number of entries in the maps is fixed regardless of the input size.
Approach 3: Frequency Mapping
We use two frequency objects
sFreq
andtFreq
to keep track of the last seen index for each character in the stringss
andt
.The code iterates through the characters in the strings and checks if the last seen index of the current character in
s
matches the last seen index of the corresponding character int
.If they don’t match, the strings are not isomorphic, and the function returns
false
. Otherwise, it updates the frequency objects with the current index.
// ES6 Arrow Function
const isIsomorphic = (s, t) => {
if(s.length !== t.length) return false;
let sFreq = {}, tFreq = {};
for(let i = 0; i < s.length; i++) {
const charS = s[i];
const charT = t[i];
if(sFreq[charS] !== tFreq[charT]) return false;
sFreq[charS] = i + 1;
tFreq[charT] = i + 1;
}
return true;
}
Time Complexity: O(N)
We iterate through the characters of both input strings once. Therefore, the time complexity is linear.
Space Complexity: O(1)
The space complexity constant because the frequency objects sFreq
and tFreq
have a fixed size of at most 26 entries and are independent of the input size.
And there you have it guys! We’ve explored two different approaches, optimized our solutions, and hopefully had some fun along the way. I hope this article has provided you with valuable insights and helped you better understand the different approaches to solving this problem. Happy coding!
Problem - Leetcode 205