Category: Easy | Concepts used: Regular expressions, string tokenization, whitespace compaction, boundary conditions
Problem Statement
Given a sentence, count how many words it contains (words are separated by spaces).
Input : "Hello World" Output: 2
Input : "This is a test" Output: 4
Examples (with edge scenarios)
| # | Input | Output | Why |
|---|---|---|---|
| 1 | "Hello World" | 2 | Two words, single spacing |
| 2 | "" | 0 | Empty input contains no words |
| 3 | " " | 0 | Contains only whitespace |
| 4 | "Hello World" | 2 | Consecutive spaces should not create empty tokens |
| 5 | " Hello World " | 2 | Leading and trailing spaces must be ignored |
โ ๏ธ Common Beginner Mistake
Mistake Impact Fix Using sentence.split(" ").lengthdirectlyConsecutive spaces yield empty strings ( "") in the split array, inflating the countTrim the string, then split using \\s+Not checking for an empty/only-spaces string after trimming "".split(" ")returns an array containing one empty element, returning a word count of1instead of0Explicitly verify trimmed.isEmpty()and return0
Before You Code: Clarify the Contract
Before choosing an algorithm, confirm how null and empty strings should behave, whether comparison is case-sensitive, and whether spaces or punctuation count. Java char values are UTF-16 code units, not always complete human-visible Unicode characters, so international text may require code points or grapheme-aware libraries.
Analogy: Counting Islands on a Map
Imagine you are a cartographer looking at a map:
- The landmasses represent words, and the water represents whitespace.
- Goal: Count the total number of distinct islands.
- Naive approach: You count every change from land to water, including small puddles or docks. Extra spaces confuse you.
- Correct approach: You trim away the coastal waters at the very edges of the map (
trim()). Then, you treat any continuous block of water as a single divider separating the islands (\\s+). It doesnโt matter if an island is separated by 1 meter of water or 100 meters (1 space vs 3 spaces) โ it is still a single water barrier separating two distinct landmasses!
Solution 1 โ trim() + split(โ\s+โ) (Recommended)
This is the standard regular expression approach.
Intuition
By first stripping outer spaces via trim(), we clean the boundaries. We then split using the regex pattern \\s+. In regular expressions:
\\smatches any whitespace character (space, tab, newline).+specifies โone or moreโ occurrences. This ensures consecutive spaces are merged and treated as a single separator.
public class CountWords {
public static int countWords(String sentence) {
if (sentence == null) {
return 0;
}
String trimmed = sentence.trim(); // Strip outer padding
if (trimmed.isEmpty()) {
return 0; // Guard against empty or whitespace-only inputs
}
// Split on one or more spaces, tabs, or newlines
String[] words = trimmed.split("\\s+");
return words.length;
}
public static void main(String[] args) {
System.out.println(countWords("Hello World")); // 2
System.out.println(countWords("")); // 0
System.out.println(countWords(" ")); // 0
System.out.println(countWords("Hello World")); // 2
System.out.println(countWords(" Hello World ")); // 2
}
}
Output:
2
0
0
2
2
Dry Run (sentence = โ Hello World โ)
trimmed = "Hello World"
trimmed.isEmpty() -> false
words = trimmed.split("\\s+") -> ["Hello", "World"]
Result = 2
Solution 2 โ Naive split(โ โ) (Shown for Comparison)
This approach splits strictly on the single space character ' '.
Intuition
A simple split by space looks clean but fails to handle irregular layouts, counting blank tokens as actual words.
public class CountWordsNaive {
public static int countWords(String sentence) {
if (sentence == null || sentence.isEmpty()) {
return 0;
}
return sentence.split(" ").length; // No trimming, splits on single space
}
public static void main(String[] args) {
System.out.println(countWords("Hello World")); // 2 (works)
System.out.println(countWords("Hello World")); // 4 (WRONG - counts empty tokens)
}
}
๐ Visual Flowchart
graph TD
Start["Given Sentence S"] --> NullCheck{"S is null?"}
NullCheck -->|Yes| RetZero["Return 0"]
NullCheck -->|No| Trim["trimmed = S.trim()"]
Trim --> EmptyCheck{"trimmed.isEmpty()?"}
EmptyCheck -->|Yes| RetZero
EmptyCheck -->|No| Split["words = trimmed.split('\\s+')"]
Split --> End["Return words.length"]
Interviewer Insights
This is a fundamental string manipulation question that tests awareness of regex parsing limits.
Follow-up questions you might get:
- โWhat does \s+ represent in detail?โ โ Explain that
\\smatches any whitespace character (equivalent to[ \t\n\x0B\f\r]), while the+quantifier matches one or more consecutive occurrences. - โCan you solve this without using split() or regular expressions to save memory?โ โ Yes. We can traverse the string and count word transitions in a single pass. A word starts when we transition from a whitespace character to a non-whitespace character:
Interview Tip: Explain that this manual loop is much more efficient than Solution 1 because it runs in (O(1)) auxiliary memory. It avoids allocating string arrays or compiling regex patterns.public static int countWordsManual(String sentence) { if (sentence == null) return 0; int count = 0; boolean inWord = false; for (int i = 0; i < sentence.length(); i++) { char ch = sentence.charAt(i); if (Character.isWhitespace(ch)) { inWord = false; // We hit whitespace } else if (!inWord) { inWord = true; // Transitioned from space to letter -> word start! count++; } } return count; }
Quick Recap
| Approach | Space Complexity (Auxiliary) | Time Complexity | Handles Irregular Spacing? | Interview Signal |
|---|---|---|---|---|
trim + split | (O(N)) | (O(N)) | Yes | Good, standard regex approach |
| Manual Loop | (O(1)) | (O(N)) | Yes | Outstanding memory awareness and pointer tracking |
Naive split(" ") | (O(N)) | (O(N)) | No | Poor edge-case verification |
- Author
- TechByteByByte Editorial Team
- Reviewed by
- TechByteByByte Admin
- Published
- Last reviewed