Given an array of strings words
, return the words that can be typed using letters of the alphabet on only one row of American keyboard like the image below.
Note that the strings are case-insensitive, both lowercased and uppercased of the same letter are treated as if they are at the same row.
In the American keyboard:
"qwertyuiop"
,"asdfghjkl"
, and"zxcvbnm"
.
Example 1:
Input: words = ["Hello","Alaska","Dad","Peace"]
Output: ["Alaska","Dad"]
Explanation:
Both "a"
and "A"
are in the 2nd row of the American keyboard due to case insensitivity.
Example 2:
Input: words = ["omk"]
Output: []
Example 3:
Input: words = ["adsdf","sfd"]
Output: ["adsdf","sfd"]
Constraints:
1 <= words.length <= 20
1 <= words[i].length <= 100
words[i]
consists of English letters (both lowercase and uppercase). This problem asks to filter a list of words, keeping only those that can be typed using letters from a single row of a standard QWERTY keyboard. The solution leverages the observation that letters belonging to the same row can be identified by a common numerical index.
The solutions use a mapping technique to efficiently determine which row each character belongs to. Instead of using multiple set
comparisons (Solution 1), or multiple conditional checks, a single string s
is used. This string's index corresponds to the character's position in the alphabet (a
to z
), and its value represents the keyboard row (1, 2, or 3).
The algorithm iterates through each word in the input list:
s
.Time Complexity: O(N*M), where N is the number of words, and M is the maximum length of a word. The algorithm iterates through each word and its characters.
Space Complexity: O(1). The space used by the s
string and the result list is constant and doesn't depend on the input size. Note that in Solution 1, using sets has a small constant space overhead.
This solution is concise and efficient:
class Solution:
def findWords(self, words: List[str]) -> List[str]:
ans = []
s = "12210111011122000010020202" # Mapping string: index corresponds to char, value to row
for w in words:
x = s[ord(w[0].lower()) - ord('a')] # get the row number for the first character.
if all(s[ord(c.lower()) - ord('a')] == x for c in w): # Check if all chars are in same row
ans.append(w)
return ans
The all()
function efficiently checks if all characters in the word belong to the same row. The ord()
function converts characters to their ASCII values for index calculation. Lowercasing ensures case-insensitivity.
This Java solution utilizes a similar approach but is slightly more verbose:
class Solution {
public String[] findWords(String[] words) {
String s = "12210111011122000010020202";
List<String> ans = new ArrayList<>();
for (var w : words) {
String t = w.toLowerCase();
char x = s.charAt(t.charAt(0) - 'a'); // Get the row for the first character
boolean ok = true;
for (char c : t.toCharArray()) {
if (s.charAt(c - 'a') != x) { // Check if all chars are in the same row.
ok = false;
break;
}
}
if (ok) {
ans.add(w);
}
}
return ans.toArray(new String[0]);
}
}
The logic remains the same: find the row of the first character and then verify that all other characters belong to the same row. The use of toCharArray()
iterates through the word. The result is converted from a List
to a String[]
for return. Similar approaches are used in other languages provided.