Isomorphic Strings
Problem
s and t are isomorphic if you
can rename the characters of s to obtain t, where each character is
always renamed the same way and no two different characters receive the same new name
(a character may map to itself). Return whether s and t are
isomorphic.s="egg", t="add"Outputtrues="foo", t="bar"Outputfalses="badc", t="baba"Outputfalse1 ≤ n ≤ 5·104 · t.length == s.length · any valid ASCII characters
Try it yourself
class Solution {
public boolean isIsomorphic(String s, String t) {
}
}
s→t and t→s). Equivalently, at every index the
two characters must have been last seen at the same position.Instinct Need "have I seen this before?", "how many times?" or "group things that look alike"? Trade memory for O(1) lookups with a HashMap, HashSet or int[26].
Memory hack Don't search, remember - every element checks in at the front desk, and the desk answers instantly.
Open the Hashing & Counting guide →Approaches
Brute ForceCompare Every Pair of Indices
O(n2)Space O(1)A valid renaming exists iff for every pair i < j,
s[i]==s[j] exactly when t[i]==t[j]. Check all pairs; any
mismatch means one char maps to two, or two chars map to one.
public boolean isIsomorphic(String s, String t) { if (s.length() != t.length()) return false; for (int i = 0; i < s.length(); i++) { for (int j = 0; j < i; j++) { boolean sameS = s.charAt(i) == s.charAt(j); boolean sameT = t.charAt(i) == t.charAt(j); if (sameS != sameT) return false; } } return true; }
No extra memory and easy to argue, but quadratic.
BetterTwo Hash Maps (s→t and t→s)
O(n)Space O(k)Record the first mapping seen for each char in mapST and, to forbid
two chars sharing a target, the reverse in mapTS. Any conflict in either
map means no bijection. k = distinct characters (≤ 128).
public boolean isIsomorphic(String s, String t) { if (s.length() != t.length()) return false; Map<Character, Character> mapST = new HashMap<>(); Map<Character, Character> mapTS = new HashMap<>(); for (int i = 0; i < s.length(); i++) { char a = s.charAt(i), b = t.charAt(i); Character prevB = mapST.putIfAbsent(a, b); Character prevA = mapTS.putIfAbsent(b, a); if ((prevB != null && prevB != b) || (prevA != null && prevA != a)) { return false; } } return true; }
Linear and general, but boxing/hashing adds constant-factor overhead.
OptimalLast-Seen Index Arrays
O(n)Space O(1)Store, for each character, the last index (+1) where it appeared. If
s[i] and t[i] are a consistent pair, they were last seen
together, so lastS[s[i]] == lastT[t[i]]. Storing i+1 keeps
0 meaning “never seen”.
public boolean isIsomorphic(String s, String t) { if (s.length() != t.length()) return false; int[] lastS = new int[256]; int[] lastT = new int[256]; for (int i = 0; i < s.length(); i++) { char a = s.charAt(i), b = t.charAt(i); if (lastS[a] != lastT[b]) return false; lastS[a] = i + 1; // +1 so 0 means "unseen" lastT[b] = i + 1; } return true; }
Dry runs="paper", t="title" (cells show s/t pair)
- i=0p/tia/ip/te/lr/e0==0, set both=1
- i=1p/ta/iip/te/lr/e0==0, set both=2
- i=2p/ta/ip/tie/lr/ep:1 == t:1 ✔
- i=4p/ta/ip/te/lr/eir:0 == e:0 → true
Single pass with two fixed 256-int arrays — fastest in practice.
At a glance
| Level | Approach | Time | Space |
|---|---|---|---|
| Brute Force | Compare Every Pair of Indices | O(n2) | O(1) |
| Better | Two Hash Maps (s→t and t→s) | O(n) | O(k) |
| Optimal | Last-Seen Index Arrays | O(n) | O(1) |
- Checking only
s→t:"ab"/"aa"would wrongly pass. - Storing
iinstead ofi+1— index 0 is confused with “unseen”. - Sizing arrays to 26 — the input can be any ASCII character, not just lowercase.
- Say the word bijection early: one-to-one in both directions.
- Test
"badc"/"baba": each one-way map looks fine, the reverse catches it. - Note the trick generalises to Word Pattern (290) by mapping words instead of chars.