185 lines
8.7 KiB
YAML
185 lines
8.7 KiB
YAML
title: Check If a Word Occurs As a Prefix of Any Word in a Sentence
|
|
slug: check-if-word-occurs-as-prefix
|
|
difficulty: easy
|
|
leetcode_id: 1455
|
|
leetcode_url: https://leetcode.com/problems/check-if-a-word-occurs-as-a-prefix-of-any-word-in-a-sentence/
|
|
categories:
|
|
- strings
|
|
patterns:
|
|
- slug: two-pointers
|
|
is_optimal: true
|
|
|
|
function_signature: "def is_prefix_of_word(sentence: str, search_word: str) -> int:"
|
|
|
|
test_cases:
|
|
visible:
|
|
- input: { sentence: "i love eating burger", search_word: "burg" }
|
|
expected: 4
|
|
- input: { sentence: "this problem is an easy problem", search_word: "pro" }
|
|
expected: 2
|
|
- input: { sentence: "i am tired", search_word: "you" }
|
|
expected: -1
|
|
hidden:
|
|
- input: { sentence: "hello", search_word: "hello" }
|
|
expected: 1
|
|
- input: { sentence: "hello world", search_word: "world" }
|
|
expected: 2
|
|
- input: { sentence: "a b c", search_word: "a" }
|
|
expected: 1
|
|
- input: { sentence: "abc def ghi", search_word: "xyz" }
|
|
expected: -1
|
|
- input: { sentence: "hellohello", search_word: "ell" }
|
|
expected: -1
|
|
|
|
description: |
|
|
Given a `sentence` that consists of some words separated by a **single space**, and a `searchWord`, check if `searchWord` is a prefix of any word in `sentence`.
|
|
|
|
Return *the index of the word in* `sentence` *(**1-indexed**) where* `searchWord` *is a prefix of this word*. If `searchWord` is a prefix of more than one word, return the index of the first word **(minimum index)**. If there is no such word return `-1`.
|
|
|
|
A **prefix** of a string `s` is any leading contiguous substring of `s`.
|
|
|
|
constraints: |
|
|
- `1 <= sentence.length <= 100`
|
|
- `1 <= searchWord.length <= 10`
|
|
- `sentence` consists of lowercase English letters and spaces.
|
|
- `searchWord` consists of lowercase English letters.
|
|
|
|
examples:
|
|
- input: 'sentence = "i love eating burger", searchWord = "burg"'
|
|
output: "4"
|
|
explanation: '"burg" is prefix of "burger" which is the 4th word in the sentence.'
|
|
- input: 'sentence = "this problem is an easy problem", searchWord = "pro"'
|
|
output: "2"
|
|
explanation: '"pro" is prefix of "problem" which is the 2nd and the 6th word in the sentence, but we return 2 as it''s the minimal index.'
|
|
- input: 'sentence = "i am tired", searchWord = "you"'
|
|
output: "-1"
|
|
explanation: '"you" is not a prefix of any word in the sentence.'
|
|
|
|
explanation:
|
|
intuition: |
|
|
Think of the sentence as a sequence of words laid out in a row, like books on a shelf. You're looking for the first book whose title *starts with* a specific set of letters.
|
|
|
|
The key insight is that we don't need any fancy string matching algorithms here. The problem is straightforward: split the sentence into individual words, then check each word one by one to see if it begins with `searchWord`.
|
|
|
|
Since we want the **first** matching word, we iterate through the words in order and return immediately when we find a match. This "early return" pattern is efficient because we stop as soon as we find what we're looking for.
|
|
|
|
The prefix check itself is simple: a word has `searchWord` as a prefix if the first `len(searchWord)` characters of the word exactly match `searchWord`. Most languages provide a built-in `startswith()` or similar method for this.
|
|
|
|
approach: |
|
|
We solve this using a **Linear Scan with Prefix Check**:
|
|
|
|
**Step 1: Split the sentence into words**
|
|
|
|
- Use the space character as the delimiter to break the sentence into a list of words
|
|
- This gives us direct access to each word by index
|
|
|
|
|
|
|
|
**Step 2: Iterate through words with their indices**
|
|
|
|
- For each word, check if it starts with `searchWord`
|
|
- Python's `str.startswith()` method handles the prefix comparison efficiently
|
|
- Remember the problem uses **1-indexed** positions, so we need to add 1 to the 0-based index
|
|
|
|
|
|
|
|
**Step 3: Return the result**
|
|
|
|
- If a word matches, return its 1-indexed position immediately
|
|
- If no word matches after checking all, return `-1`
|
|
|
|
|
|
|
|
This approach works because we process words in order, guaranteeing we find the minimum index first.
|
|
|
|
common_pitfalls:
|
|
- title: Off-by-One Error with 1-Indexing
|
|
description: |
|
|
The problem explicitly states that word positions are **1-indexed**, not 0-indexed. Many programming languages use 0-based indexing by default.
|
|
|
|
If you iterate with a loop like `for i in range(len(words))` and return `i` directly, you'll be off by one. The first word should return `1`, not `0`.
|
|
|
|
Always add 1 to convert from 0-indexed to 1-indexed, or use `enumerate(words, start=1)`.
|
|
wrong_approach: "Returning 0-indexed position directly"
|
|
correct_approach: "Add 1 to convert to 1-indexed position"
|
|
|
|
- title: Manual Prefix Checking Errors
|
|
description: |
|
|
If you implement prefix checking manually instead of using built-in methods, you might forget to check if the word is long enough.
|
|
|
|
For example, checking `word[:len(searchWord)] == searchWord` works, but manually comparing character-by-character without bounds checking could cause index out of range errors when the word is shorter than the search word.
|
|
|
|
Using `startswith()` handles all edge cases automatically.
|
|
wrong_approach: "Manual character comparison without length check"
|
|
correct_approach: "Use built-in startswith() or slice with length validation"
|
|
|
|
- title: Forgetting Empty String Edge Cases
|
|
description: |
|
|
While the constraints guarantee non-empty inputs, in a real interview you might encounter edge cases like empty sentences or empty search words.
|
|
|
|
An empty `searchWord` is technically a prefix of every word. Be prepared to discuss how you'd handle these cases if the constraints were relaxed.
|
|
|
|
key_takeaways:
|
|
- "**Use built-in methods**: `startswith()` is cleaner and handles edge cases better than manual prefix checking"
|
|
- "**Early return pattern**: Stop iterating as soon as you find the answer to avoid unnecessary work"
|
|
- "**Watch for indexing conventions**: Problems often specify 1-indexed results when natural language is involved"
|
|
- "**String splitting is your friend**: Converting a sentence to a word list simplifies iteration and access"
|
|
|
|
time_complexity: "O(n * m). We iterate through each character of the sentence once during split (O(n)), then for each word we may compare up to `m` characters where `m` is the length of `searchWord`. In the worst case, we check all words."
|
|
space_complexity: "O(n). We store the list of words after splitting, which in the worst case contains all characters from the original sentence."
|
|
|
|
solutions:
|
|
- approach_name: Linear Scan with startswith()
|
|
is_optimal: true
|
|
code: |
|
|
def is_prefix_of_word(sentence: str, search_word: str) -> int:
|
|
# Split sentence into list of words
|
|
words = sentence.split()
|
|
|
|
# Check each word (enumerate with start=1 for 1-indexed result)
|
|
for i, word in enumerate(words, start=1):
|
|
# If this word starts with searchWord, return its position
|
|
if word.startswith(search_word):
|
|
return i
|
|
|
|
# No word had the prefix
|
|
return -1
|
|
explanation: |
|
|
**Time Complexity:** O(n * m) — Where n is the sentence length and m is the search word length. Split is O(n), and each startswith check is O(m) in the worst case.
|
|
|
|
**Space Complexity:** O(n) — The words list stores all characters from the sentence.
|
|
|
|
This is the cleanest and most Pythonic solution. Using `enumerate` with `start=1` handles the 1-indexing requirement elegantly.
|
|
|
|
- approach_name: Manual Iteration Without Split
|
|
is_optimal: false
|
|
code: |
|
|
def is_prefix_of_word(sentence: str, search_word: str) -> int:
|
|
word_index = 1 # 1-indexed word counter
|
|
word_start = 0 # Start position of current word
|
|
|
|
i = 0
|
|
while i <= len(sentence):
|
|
# End of word: either space or end of sentence
|
|
if i == len(sentence) or sentence[i] == ' ':
|
|
# Extract current word
|
|
word = sentence[word_start:i]
|
|
|
|
# Check if searchWord is a prefix
|
|
if word.startswith(search_word):
|
|
return word_index
|
|
|
|
# Move to next word
|
|
word_index += 1
|
|
word_start = i + 1
|
|
|
|
i += 1
|
|
|
|
return -1
|
|
explanation: |
|
|
**Time Complexity:** O(n * m) — Same as the split approach.
|
|
|
|
**Space Complexity:** O(w) — Where w is the length of the longest word (for the substring extraction).
|
|
|
|
This approach avoids creating a list of all words upfront, potentially saving memory for very long sentences. However, it's more complex and the savings are negligible given the problem constraints.
|