diff --git a/explanations/290/en.md b/explanations/290/en.md new file mode 100644 index 0000000..b0f25f7 --- /dev/null +++ b/explanations/290/en.md @@ -0,0 +1,131 @@ +## 290. Word Pattern [Easy] + +https://leetcode.com/problems/word-pattern + +## Description +Given a `pattern` and a string `s`, find if `s` follows the same pattern. + +Here **follow** means a full match, such that there is a bijection between a letter in `pattern` and a **non-empty** word in `s`. Specifically: + +- Each letter in `pattern` maps to **exactly** one unique word in `s`. +- Each unique word in `s` maps to **exactly** one letter in `pattern`. +- No two letters map to the same word, and no two words map to the same letter. + +**Examples** + +```tex +Example 1: +Input: pattern = "abba", s = "dog cat cat dog" +Output: true +Explanation: +The bijection can be established as: +- 'a' maps to "dog". +- 'b' maps to "cat". + +Example 2: +Input: pattern = "abba", s = "dog cat cat fish" +Output: false + +Example 3: +Input: pattern = "aaaa", s = "dog cat cat dog" +Output: false +``` + +**Constraints** +```tex +- 1 <= pattern.length <= 300 +- pattern contains only lower-case English letters +- 1 <= s.length <= 3000 +- s contains only lowercase English letters and spaces ' ' +- s does not contain any leading or trailing spaces +- All the words in s are separated by a single space +``` + +## Explanation + +### Strategy +Let's restate the problem: You're given a pattern string (like "abba") and a string of words (like "dog cat cat dog"), and you need to determine if the words follow the same pattern. This means there should be a one-to-one mapping between letters in the pattern and words in the string. + +This is a **hash table problem** that requires tracking bidirectional mappings between pattern characters and words, similar to the isomorphic strings problem but with words instead of individual characters. + +**What is given?** A pattern string and a string of space-separated words. + +**What is being asked?** Determine if the words follow the same pattern as the given pattern string. + +**Constraints:** The pattern can be up to 300 characters, and the string can be up to 3000 characters with words separated by single spaces. + +**Edge cases:** +- Pattern and words have different lengths +- Empty pattern or empty string +- Pattern with repeated characters +- String with repeated words + +**High-level approach:** +The solution involves using two hash maps to track character-to-word and word-to-character mappings, ensuring that the bijection property is maintained. + +**Decomposition:** +1. **Split the string into words**: Convert the space-separated string into a list of words +2. **Check length consistency**: If pattern length doesn't match word count, return false +3. **Create mapping dictionaries**: Track character-to-word and word-to-character mappings +4. **Verify bijection**: Ensure each character maps to exactly one word and vice versa + +**Brute force vs. optimized strategy:** +- **Brute force**: Try all possible mappings. This is extremely inefficient. +- **Optimized**: Use hash tables to track mappings in a single pass. This takes O(n) time. + +### Steps +Let's walk through the solution step by step using the first example: `pattern = "abba"`, `s = "dog cat cat dog"` + +**Step 1: Split the string into words** +- `s = "dog cat cat dog"` +- `words = ["dog", "cat", "cat", "dog"]` + +**Step 2: Check length consistency** +- `pattern.length = 4` +- `words.length = 4` +- Lengths match ✓ + +**Step 3: Initialize mapping dictionaries** +- `char_to_word = {}` (maps pattern characters to words) +- `word_to_char = {}` (maps words to pattern characters) + +**Step 4: Check first character-word pair** +- `pattern[0] = 'a'`, `words[0] = "dog"` +- Check if 'a' is already mapped: No +- Check if "dog" is already mapped: No +- Add mappings: `char_to_word['a'] = "dog"`, `word_to_char["dog"] = 'a'` + +**Step 5: Check second character-word pair** +- `pattern[1] = 'b'`, `words[1] = "cat"` +- Check if 'b' is already mapped: No +- Check if "cat" is already mapped: No +- Add mappings: `char_to_word['b'] = "cat"`, `word_to_char["cat"] = 'b'` + +**Step 6: Check third character-word pair** +- `pattern[2] = 'b'`, `words[2] = "cat"` +- Check if 'b' is already mapped: Yes, to "cat" +- Verify consistency: `char_to_word['b'] == "cat"` ✓ +- Check if "cat" is already mapped: Yes, to 'b' +- Verify consistency: `word_to_char["cat"] == 'b'` ✓ + +**Step 7: Check fourth character-word pair** +- `pattern[3] = 'a'`, `words[3] = "dog"` +- Check if 'a' is already mapped: Yes, to "dog" +- Verify consistency: `char_to_word['a'] == "dog"` ✓ +- Check if "dog" is already mapped: Yes, to 'a' +- Verify consistency: `word_to_char["dog"] == 'a'` ✓ + +**Step 8: Result** +- All character-word pairs are consistent +- Pattern is followed: `true` + +**Why this works:** +By maintaining mappings in both directions, we ensure that: +1. Each character in the pattern maps to exactly one word +2. Each word maps to exactly one character +3. The bijection property is maintained throughout the pattern + +> **Note:** The key insight is using bidirectional mapping to ensure the bijection property. This is similar to the isomorphic strings problem but operates on words instead of individual characters. + +**Time Complexity:** O(n) - we visit each character/word once +**Space Complexity:** O(k) - where k is the number of unique characters/words diff --git a/solutions/290/01.py b/solutions/290/01.py new file mode 100644 index 0000000..48897be --- /dev/null +++ b/solutions/290/01.py @@ -0,0 +1,42 @@ +def wordPattern(pattern, s): + """ + Determine if the string s follows the given pattern. + + Args: + pattern: str - The pattern string to match against + s: str - The string of space-separated words + + Returns: + bool - True if s follows the pattern, False otherwise + """ + # Split the string into words + words = s.split() + + # Check if pattern length matches word count + if len(pattern) != len(words): + return False + + # Create mapping dictionaries for both directions + char_to_word = {} # maps pattern characters to words + word_to_char = {} # maps words to pattern characters + + # Check each character-word pair + for i in range(len(pattern)): + char = pattern[i] + word = words[i] + + # Check if char is already mapped + if char in char_to_word: + # Verify the mapping is consistent + if char_to_word[char] != word: + return False + else: + # Check if word is already mapped to by another character + if word in word_to_char: + return False + + # Add new mapping + char_to_word[char] = word + word_to_char[word] = char + + return True