File: number-of-valid-words-in-a-sentence/solution.py

Date: 2026-06-06

Time: 18:25

Purpose

This file implements LeetCode 2047: Number of Valid Words in a Sentence. It owns the complete solution and its test suite in a single module — the standard structure across this repository where each problem directory contains solution.py with both the Solution class and unittest-based tests.

The problem: given a sentence string, split it into tokens by spaces and count how many tokens qualify as "valid words" under a specific set of lexical rules.

Key Components

Solution.countValidWords(sentence: str) -> int

The public entry point. Splits the sentence on whitespace, filters out empty tokens, and sums the results of is_valid() on each token.

is_valid(token: str) -> bool (nested closure)

The core validation logic. A token is valid if all of the following hold:

| Rule | Implementation |

|------|----------------|

| No digits | c.isdigit() → immediate return False |

| At most one hyphen | hyphen_count tracked; >1 → False |

| Hyphen not at start/end | i == 0 or i == len(token) - 1False |

| Hyphen flanked by letters | token[i-1].isalpha() and token[i+1].isalpha() required |

| At most one punctuation (!, ., ,) | punct_count tracked; >1 → False |

| Punctuation only at end | i != len(token) - 1False |

TestCountValidWords

14 test cases covering the three LeetCode examples plus edge cases: standalone punctuation, leading/trailing hyphens, multiple hyphens, multiple punctuation, mid-word punctuation, digits, whitespace-only input, single letter, and punctuation-before-hyphen combinations.

Patterns

Dependencies

Imports: Only unittest from the standard library. No external packages.

Imported by: The "Imported By" list in the prompt is misleading — it lists hundreds of unrelated test files. That's likely an artifact of the analysis tool treating all testsolution.py files as importing from every solution.py. The actual import graph is self-contained: number-of-valid-words-in-a-sentence/testsolution.py imports from this file.

Flow

1. sentence.split() tokenizes on whitespace (one or more spaces)

2. For each non-empty token, is_valid walks every character left-to-right

3. Any digit → reject immediately

4. Hyphen rules checked on encounter: count, position, neighbors

5. Punctuation rules checked on encounter: count, must be final character

6. If the loop completes without rejection, the token is valid

7. Valid token count returned as the final integer

Invariants

Error Handling

None. The function assumes well-formed input per the LeetCode contract (lowercase letters, digits, hyphens, !.,, and spaces). No exceptions are raised or caught. The unittest harness uses assertEqual assertions — failures surface as test errors, not runtime exceptions.

Topics to Explore

Beliefs