Word Pattern
Check whether a pattern's characters bijectively match words in a sentence.
4 min read
hash-map strings
Problem#
Given a pattern of characters and a space-separated string s, return true iff there is a bijection between pattern chars and words such that pattern[i] maps to the i-th word.
Examples#
pattern = "abba", s = "dog cat cat dog"→true.pattern = "abba", s = "dog cat cat fish"→false.pattern = "aaaa", s = "dog cat cat dog"→false.
Constraints#
1 <= pattern.length <= 300.sis non-empty lowercase words separated by single spaces.
Hints#
Hint
Bijection means two maps: char->word and word->char.
Approach#
Split s into words; require the count to match pattern.size(). Two maps enforce both directions of the bijection; conflict on either side returns false.
Solution#
class Solution {public: bool wordPattern(string pattern, string s) { vector<string> words; string cur; s.push_back(' '); for (char c : s) { if (c == ' ') { if (!cur.empty()) { words.push_back(std::move(cur)); cur.clear(); } } else cur.push_back(c); } if (words.size() != pattern.size()) return false; unordered_map<char,string> c2w; unordered_map<string,char> w2c; for (int i = 0; i < (int)pattern.size(); ++i) { char p = pattern[i]; const string& w = words[i]; auto itC = c2w.find(p); auto itW = w2c.find(w); if (itC == c2w.end() && itW == w2c.end()) { c2w[p] = w; w2c[w] = p; } else if (itC == c2w.end() || itW == w2c.end()) return false; else if (itC->second != w || itW->second != p) return false; } return true; }};import "strings"
func wordPattern(pattern string, s string) bool { words := strings.Fields(s) if len(words) != len(pattern) { return false } c2w := map[byte]string{} w2c := map[string]byte{} for i := 0; i < len(pattern); i++ { p := pattern[i] w := words[i] cw, okC := c2w[p] wc, okW := w2c[w] if !okC && !okW { c2w[p] = w w2c[w] = p } else if !okC || !okW { return false } else if cw != w || wc != p { return false } } return true}class Solution: def wordPattern(self, pattern: str, s: str) -> bool: words = s.split() if len(words) != len(pattern): return False c2w: dict[str, str] = {} w2c: dict[str, str] = {} for p, w in zip(pattern, words): in_c, in_w = p in c2w, w in w2c if not in_c and not in_w: c2w[p] = w w2c[w] = p elif not in_c or not in_w: return False elif c2w[p] != w or w2c[w] != p: return False return Truefunction wordPattern(pattern, s) { const words = s.split(/\s+/).filter(Boolean); if (words.length !== pattern.length) return false; const c2w = new Map(); const w2c = new Map(); for (let i = 0; i < pattern.length; i++) { const p = pattern[i], w = words[i]; const inC = c2w.has(p), inW = w2c.has(w); if (!inC && !inW) { c2w.set(p, w); w2c.set(w, p); } else if (!inC || !inW) { return false; } else if (c2w.get(p) !== w || w2c.get(w) !== p) { return false; } } return true;}class Solution { public boolean wordPattern(String pattern, String s) { String[] words = s.split(" "); if (words.length != pattern.length()) return false; Map<Character, String> c2w = new HashMap<>(); Map<String, Character> w2c = new HashMap<>(); for (int i = 0; i < pattern.length(); i++) { char p = pattern.charAt(i); String w = words[i]; boolean inC = c2w.containsKey(p); boolean inW = w2c.containsKey(w); if (!inC && !inW) { c2w.put(p, w); w2c.put(w, p); } else if (!inC || !inW) { return false; } else if (!c2w.get(p).equals(w) || w2c.get(w) != p) { return false; } } return true; }}function wordPattern(pattern: string, s: string): boolean { const words = s.split(/\s+/).filter(Boolean); if (words.length !== pattern.length) return false; const c2w = new Map<string, string>(); const w2c = new Map<string, string>(); for (let i = 0; i < pattern.length; i++) { const p = pattern[i], w = words[i]; const inC = c2w.has(p), inW = w2c.has(w); if (!inC && !inW) { c2w.set(p, w); w2c.set(w, p); } else if (!inC || !inW) { return false; } else if (c2w.get(p) !== w || w2c.get(w) !== p) { return false; } } return true;}Editorial#
Same shape as Isomorphic Strings — only the “alphabet” on one side is words instead of single characters. Either-side absence with the other present is a contradiction (the mapping would be many-to-one), hence the explicit handling. Tokenize first to avoid index gymnastics.
Complexity#
- Time: O(N).
- Space: O(N).
Concept revision#
Related#