A clear explanation of filtering words that can be typed using only one row of an American keyboard.
Problem Restatement
We are given an array of strings words.
Return the words that can be typed using letters from only one row of an American keyboard.
The keyboard rows are:
| Row | Letters |
|---|---|
| First row | qwertyuiop |
| Second row | asdfghjkl |
| Third row | zxcvbnm |
Letter case does not matter. For example, A and a belong to the same row. The word must use letters from only one row. The original word should be returned with its original casing.
Input and Output
| Item | Meaning |
|---|---|
| Input | Array of strings words |
| Output | Words that can be typed using one keyboard row |
| Case rule | Case-insensitive check |
| Return rule | Keep original word casing |
Function shape:
def findWords(words: list[str]) -> list[str]:
...Examples
Example 1:
words = ["Hello", "Alaska", "Dad", "Peace"]Check each word:
| Word | Rows used | Valid |
|---|---|---|
"Hello" | multiple rows | no |
"Alaska" | second row only | yes |
"Dad" | second row only | yes |
"Peace" | multiple rows | no |
Answer:
["Alaska", "Dad"]Example 2:
words = ["omk"]The letters are not all from one row.
Answer:
[]Example 3:
words = ["adsdf", "sfd"]Both words use only the second row.
Answer:
["adsdf", "sfd"]First Thought: Check Each Row
For each word, we can check whether all its letters are in one of the three keyboard rows.
Represent each row as a set:
set("qwertyuiop")
set("asdfghjkl")
set("zxcvbnm")Then convert the word to lowercase and check membership.
Key Insight
A word is valid if the set of its lowercase letters is a subset of one keyboard row.
For example:
word = "Alaska"
letters = set(word.lower())The set is:
{"a", "l", "s", "k"}All of these letters are in:
set("asdfghjkl")So "Alaska" is valid.
For "Hello":
{"h", "e", "l", "o"}These letters are split across rows, so it is invalid.
Algorithm
Create three sets, one for each keyboard row.
For each word:
- Convert it to lowercase.
- Convert its letters to a set.
- Check whether this set is a subset of any keyboard row.
- If yes, append the original word to the answer.
Return the answer.
Correctness
The algorithm checks each word after converting it to lowercase, so uppercase and lowercase letters are treated the same.
For a word to be typed using one row, every distinct letter in that word must belong to the same row. Converting the word to a set of letters captures exactly the distinct letters used by the word.
If this letter set is a subset of one keyboard row set, then every letter in the word can be typed from that row, so the word is valid.
If the letter set is not a subset of any row, then the word uses letters from at least two rows, so it cannot be typed using one row.
Therefore, the algorithm returns exactly the valid words.
Complexity
Let:
| Symbol | Meaning |
|---|---|
n | number of words |
L | total number of characters across all words |
| Metric | Value | Why |
|---|---|---|
| Time | O(L) | Each character is processed a constant number of times |
| Space | O(1) excluding output | Keyboard row sets have fixed size |
Implementation
class Solution:
def findWords(self, words: list[str]) -> list[str]:
rows = [
set("qwertyuiop"),
set("asdfghjkl"),
set("zxcvbnm"),
]
ans = []
for word in words:
letters = set(word.lower())
if any(letters <= row for row in rows):
ans.append(word)
return ansCode Explanation
The keyboard rows are stored as sets:
rows = [
set("qwertyuiop"),
set("asdfghjkl"),
set("zxcvbnm"),
]For each word, convert it to lowercase and collect its letters:
letters = set(word.lower())This checks whether all letters come from one row:
if any(letters <= row for row in rows):The operator <= means “is subset of” for sets.
If the word is valid, append the original word:
ans.append(word)Testing
def run_tests():
s = Solution()
assert s.findWords(["Hello", "Alaska", "Dad", "Peace"]) == ["Alaska", "Dad"]
assert s.findWords(["omk"]) == []
assert s.findWords(["adsdf", "sfd"]) == ["adsdf", "sfd"]
assert s.findWords(["TYPE", "row"]) == ["TYPE", "row"]
assert s.findWords(["a", "S", "z"]) == ["a", "S", "z"]
print("all tests passed")
run_tests()Test meaning:
| Test | Why |
|---|---|
["Hello", "Alaska", "Dad", "Peace"] | Main example |
["omk"] | Invalid mixed-row word |
["adsdf", "sfd"] | All valid second-row words |
["TYPE", "row"] | Case-insensitive checking |
| Single-letter words | Any single letter is valid |