Off-by-one on range boundaries
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Build confidence with an intuition-first walkthrough focused on core interview patterns fundamentals.
A sentence consists of lowercase letters ('a' to 'z'), digits ('0' to '9'), hyphens ('-'), punctuation marks ('!', '.', and ','), and spaces (' ') only. Each sentence can be broken down into one or more tokens separated by one or more spaces ' '.
A token is a valid word if all three of the following are true:
'-'. If present, it must be surrounded by lowercase characters ("a-b" is valid, but "-ab" and "ab-" are not valid)."ab,", "cd!", and "." are valid, but "a!b" and "c.," are not valid).Examples of valid words include "a-b.", "afad", "ba-c", "a!", and "!".
Given a string sentence, return the number of valid words in sentence.
Example 1:
Input: sentence = "cat and dog" Output: 3 Explanation: The valid words in the sentence are "cat", "and", and "dog".
Example 2:
Input: sentence = "!this 1-s b8d!" Output: 0 Explanation: There are no valid words in the sentence. "!this" is invalid because it starts with a punctuation mark. "1-s" and "b8d" are invalid because they contain digits.
Example 3:
Input: sentence = "alice and bob are playing stone-game10" Output: 5 Explanation: The valid words in the sentence are "alice", "and", "bob", "are", and "playing". "stone-game10" is invalid because it contains digits.
Constraints:
1 <= sentence.length <= 1000sentence only contains lowercase English letters, digits, ' ', '-', '!', '.', and ','.1 token.Problem summary: A sentence consists of lowercase letters ('a' to 'z'), digits ('0' to '9'), hyphens ('-'), punctuation marks ('!', '.', and ','), and spaces (' ') only. Each sentence can be broken down into one or more tokens separated by one or more spaces ' '. A token is a valid word if all three of the following are true: It only contains lowercase letters, hyphens, and/or punctuation (no digits). There is at most one hyphen '-'. If present, it must be surrounded by lowercase characters ("a-b" is valid, but "-ab" and "ab-" are not valid). There is at most one punctuation mark. If present, it must be at the end of the token ("ab,", "cd!", and "." are valid, but "a!b" and "c.," are not valid). Examples of valid words include "a-b.", "afad", "ba-c", "a!", and "!". Given a string sentence, return the number of valid words in sentence.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: General problem-solving
"cat and dog"
"!this 1-s b8d!"
"alice and bob are playing stone-game10"
maximum-number-of-words-found-in-sentences)Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #2047: Number of Valid Words in a Sentence
class Solution {
public int countValidWords(String sentence) {
int ans = 0;
for (String s : sentence.split(" ")) {
ans += check(s.toCharArray());
}
return ans;
}
private int check(char[] s) {
if (s.length == 0) {
return 0;
}
boolean st = false;
for (int i = 0; i < s.length; ++i) {
if (Character.isDigit(s[i])) {
return 0;
}
if ((s[i] == '!' || s[i] == '.' || s[i] == ',') && i < s.length - 1) {
return 0;
}
if (s[i] == '-') {
if (st || i == 0 || i == s.length - 1) {
return 0;
}
if (!Character.isAlphabetic(s[i - 1]) || !Character.isAlphabetic(s[i + 1])) {
return 0;
}
st = true;
}
}
return 1;
}
}
// Accepted solution for LeetCode #2047: Number of Valid Words in a Sentence
func countValidWords(sentence string) (ans int) {
check := func(s string) int {
if len(s) == 0 {
return 0
}
st := false
for i, r := range s {
if unicode.IsDigit(r) {
return 0
}
if (r == '!' || r == '.' || r == ',') && i < len(s)-1 {
return 0
}
if r == '-' {
if st || i == 0 || i == len(s)-1 {
return 0
}
if !unicode.IsLetter(rune(s[i-1])) || !unicode.IsLetter(rune(s[i+1])) {
return 0
}
st = true
}
}
return 1
}
for _, s := range strings.Fields(sentence) {
ans += check(s)
}
return ans
}
# Accepted solution for LeetCode #2047: Number of Valid Words in a Sentence
class Solution:
def countValidWords(self, sentence: str) -> int:
def check(s: str) -> bool:
st = False
for i, c in enumerate(s):
if c.isdigit() or (c in "!.," and i < len(s) - 1):
return False
if c == "-":
if (
st
or i in (0, len(s) - 1)
or not s[i - 1].isalpha()
or not s[i + 1].isalpha()
):
return False
st = True
return True
return sum(check(s) for s in sentence.split())
// Accepted solution for LeetCode #2047: Number of Valid Words in a Sentence
struct Solution;
impl Solution {
fn count_valid_words(sentence: String) -> i32 {
let mut res = 0;
for word in sentence.split_whitespace() {
if Solution::is_valid(word) {
res += 1;
}
}
res
}
fn is_valid(s: &str) -> bool {
let n = s.len();
let mut h_count = 0;
let s: Vec<char> = s.chars().collect();
for i in 0..n {
let c = s[i];
match c {
'a'..='z' => {}
',' | '!' | '.' => {
if i != n - 1 {
return false;
}
}
'-' => {
h_count += 1;
if i == 0 || i == n - 1 {
return false;
}
if !(('a'..='z').contains(&s[i - 1]) && ('a'..='z').contains(&s[i + 1])) {
return false;
}
}
_ => {
return false;
}
};
}
if h_count > 1 {
return false;
}
true
}
}
#[test]
fn test() {
let sentence = "cat and dog".to_string();
let res = 3;
assert_eq!(Solution::count_valid_words(sentence), res);
let sentence = "!this 1-s b8d!".to_string();
let res = 0;
assert_eq!(Solution::count_valid_words(sentence), res);
let sentence = "alice and bob are playing stone-game10".to_string();
let res = 5;
assert_eq!(Solution::count_valid_words(sentence), res);
}
// Accepted solution for LeetCode #2047: Number of Valid Words in a Sentence
function countValidWords(sentence: string): number {
const check = (s: string): number => {
if (s.length === 0) {
return 0;
}
let st = false;
for (let i = 0; i < s.length; ++i) {
if (/\d/.test(s[i])) {
return 0;
}
if (['!', '.', ','].includes(s[i]) && i < s.length - 1) {
return 0;
}
if (s[i] === '-') {
if (st || [0, s.length - 1].includes(i)) {
return 0;
}
if (!/[a-zA-Z]/.test(s[i - 1]) || !/[a-zA-Z]/.test(s[i + 1])) {
return 0;
}
st = true;
}
}
return 1;
};
return sentence.split(/\s+/).reduce((acc, s) => acc + check(s), 0);
}
Use this to step through a reusable interview workflow for this problem.
Two nested loops check every pair or subarray. The outer loop fixes a starting point, the inner loop extends or searches. For n elements this gives up to n²/2 operations. No extra space, but the quadratic time is prohibitive for large inputs.
Most array problems have an O(n²) brute force (nested loops) and an O(n) optimal (single pass with clever state tracking). The key is identifying what information to maintain as you scan: a running max, a prefix sum, a hash map of seen values, or two pointers.
Review these before coding to avoid predictable interview regressions.
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.