LeetCode 193: Valid Phone Numbers Solution in Shell Explained
Filtering valid phone numbers from a text file might feel like sifting through a contact list for the right format, and LeetCode 193: Valid Phone Numbers is an easy-level challenge that makes it approachable! Given a text file file.txt
containing one phone number per line, you need to write a bash command to output only the lines with valid phone numbers, where valid formats are (xxx) xxx-xxxx
or xxx-xxx-xxxx
(x is a digit, with optional space after )
). In this blog, we’ll solve it with shell commands, exploring two solutions—grep with Regular Expression (our best solution) and awk with Pattern Matching (a practical alternative). With step-by-step examples, detailed command breakdowns, and tips, you’ll master this problem. While this is a shell challenge, you can explore Python basics at Python Basics for related coding skills. Let’s find those valid numbers!
Problem Statement
In LeetCode 193, you’re given a text file file.txt
with one phone number per line. Your task is to write a one-line bash command to:
- Read the file.
- Filter lines matching valid phone number formats:
- (xxx) xxx-xxxx (e.g., "(123) 456-7890", "(123)456-7890").
- xxx-xxx-xxxx (e.g., "123-456-7890").
- Output only the valid lines, preserving original format.
This differs from word frequency counting like LeetCode 192: Word Frequency, focusing on pattern matching rather than aggregation.
Constraints
- File exists and is readable (file.txt).
- Each line contains one phone number candidate.
- Valid formats: (xxx) xxx-xxxx or xxx-xxx-xxxx (x is 0-9, space after ) optional).
Example
Let’s see a case:
file.txt:
123-456-7890
(123) 456-7890
123 456 7890
(123)456-7890
123-4567-890
Output:
123-456-7890
(123) 456-7890
(123)456-7890
Explanation:
<ul>
<li>"123-456-7890": Matches xxx-xxx-xxxx.</li>
<li>"(123) 456-7890": Matches (xxx) xxx-xxxx.</li>
<li>"123 456 7890": Invalid format.</li>
<li>"(123)456-7890": Matches (xxx) xxx-xxxx (no space).</li>
<li>"123-4567-890": Invalid (wrong length).</li>
</ul>
This shows we’re filtering valid phone number formats.
Understanding the Problem
How do you solve LeetCode 193: Valid Phone Numbers in a bash command? We need to:
- Read file.txt line by line.
- Match each line against two patterns:
- (xxx) xxx-xxxx (parentheses, optional space, dashes).
- xxx-xxx-xxxx (dashes only).
- Output only matching lines, unchanged.
For "(123) 456-7890" and "123-456-7890", both are valid; "123 456 7890" isn’t. We need a concise command using tools like grep
or awk
with regular expressions or patterns, not a bit-counting task like LeetCode 191: Number of 1 Bits. We’ll use:
1. grep with Regular Expression: Precise, efficient—our best solution.
2. awk with Pattern Matching: Alternative approach.
Let’s dive into the best solution.
Best Solution: grep with Regular Expression Approach
Explanation
grep with Regular Expression filters valid phone numbers by:
- Reading file.txt (cat file.txt or direct grep).
- Using grep with a regular expression (-E for extended regex) to match:
- ^\([0-9]{3{\) ?[0-9]{3{-[0-9]{4{$: (xxx) xxx-xxxx or (xxx)xxx-xxxx.
- ^[0-9]{3{-[0-9]{3{-[0-9]{4{$: xxx-xxx-xxxx.
- Combining patterns with | (OR) and anchoring with ^$ for full-line match.
This ensures O(n) time (n = lines), O(1) space (grep’s internal buffer), and precision with a single command, making it the most efficient and clear solution.
For "123-456-7890":
- Matches ^[0-9]{3{-[0-9]{3{-[0-9]{4{$.
For "(123) 456-7890":
- Matches ^\([0-9]{3{\) ?[0-9]{3{-[0-9]{4{$.
Step-by-Step Example
Example: file.txt = "123-456-7890", "(123) 456-7890", "123 456 7890", "(123)456-7890", "123-4567-890"
Goal: Return valid lines.
- Step 1: Read file.
- cat file.txt: All 5 lines.
- Step 2: Apply grep -E with regex.
- Pattern: ^\([0-9]{3{\) ?[0-9]{3{-[0-9]{4{$|^[0-9]{3{-[0-9]{3{-[0-9]{4{$.
- "123-456-7890": Matches second part → valid.
- "(123) 456-7890": Matches first part (with space) → valid.
- "123 456 7890": No match (spaces, no dashes) → invalid.
- "(123)456-7890": Matches first part (no space) → valid.
- "123-4567-890": No match (wrong length) → invalid.
- Step 3: Output matches.
- "123-456-7890", "(123) 456-7890", "(123)456-7890".
- Finish: Return as required.
How the Code Works (grep with Regular Expression) – Detailed Line-by-Line Explanation
Here’s the bash command with a thorough breakdown (as a one-liner):
# Command: grep -E '^\([0-9]{3{\) ?[0-9]{3{-[0-9]{4{$|^[0-9]{3{-[0-9]{3{-[0-9]{4{$' file.txt
# Line 1: grep with extended regex
grep -E
# Use extended regex (-E) for |, ?, etc.
# Line 2: Regular expression pattern
'^\([0-9]{3{\) ?[0-9]{3{-[0-9]{4{$|^[0-9]{3{-[0-9]{3{-[0-9]{4{$'
# ^: Start of line
# \( : Literal ( (escaped)
# [0-9]{3{: 3 digits (e.g., 123)
# \): Literal )
# ?: Optional space (0 or 1)
# [0-9]{3{-: 3 digits + dash (e.g., 456-)
# [0-9]{4{: 4 digits (e.g., 7890)
# $: End of line
# |: OR
# ^[0-9]{3{-[0-9]{3{-[0-9]{4{$: 3-3-4 digits with dashes (e.g., 123-456-7890)
# Line 3: Input file
file.txt
# Source file (e.g., "123-456-7890\n(123) 456-7890")
This detailed breakdown clarifies how grep
with regex efficiently filters valid phone numbers.
Alternative: awk with Pattern Matching Approach
Explanation
awk with Pattern Matching uses awk
to match patterns:
- Read file.txt.
- Use regex in awk conditions to match both formats.
- Print lines that match either pattern.
It’s a practical alternative, O(n) time (n = lines), O(1) space, but slightly less precise than grep -E
due to awk
’s pattern syntax, though still effective.
For "(123) 456-7890":
- Matches /^\([0-9]{3{\) ?[0-9]{3{-[0-9]{4{$/.
Step-by-Step Example (Alternative)
For the same example:
- Step 1: Read file with awk.
- Step 2: Match patterns:
- "123-456-7890": /^[0-9]{3{-[0-9]{3{-[0-9]{4{$/ → valid.
- "(123) 456-7890": /^\([0-9]{3{\) ?[0-9]{3{-[0-9]{4{$/ → valid.
- "123 456 7890": No match → invalid.
- "(123)456-7890": Matches first pattern → valid.
- "123-4567-890": No match → invalid.
- Step 3: Print matches.
- Finish: Same output.
How the Code Works (awk)
# Command: awk '/^\([0-9]{3{\) ?[0-9]{3{-[0-9]{4{$|^[0-9]{3{-[0-9]{3{-[0-9]{4{$/' file.txt
awk '/^\([0-9]{3{\) ?[0-9]{3{-[0-9]{4{$|^[0-9]{3{-[0-9]{3{-[0-9]{4{$/' file.txt
Complexity
- grep with Regular Expression:
- Time: O(n) – single pass over lines.
- Space: O(1) – minimal buffer.
- awk with Pattern Matching:
- Time: O(n) – single pass.
- Space: O(1) – minimal buffer.
Efficiency Notes
grep with Regular Expression is the best solution with O(n) time and O(1) space, offering precision and clarity with a tailored regex—awk with Pattern Matching matches complexity but is less commonly used for pure filtering, making it a viable but less preferred alternative.
Key Insights
- grep -E: Regex powerhouse.
- awk: Flexible matching.
- Patterns: Format-specific.
Additional Example
file.txt:
555-555-5555
(555)555-5555
555 555 5555
Output:
555-555-5555
(555)555-5555
Explanation: Valid formats matched.
Edge Cases
- Empty File: Empty output.
- Invalid Lines: Filtered out.
- Mixed Valid/Invalid: Only valid output.
Both solutions handle these well.
Final Thoughts
LeetCode 193: Valid Phone Numbers in shell is a practical pattern-matching challenge. The grep with Regular Expression solution excels with its efficiency and specificity, while awk with Pattern Matching offers an alternative approach. Want more shell? Try LeetCode 192: Word Frequency for text processing or explore Python Basics for coding skills. Ready to practice? Solve LeetCode 193 on LeetCode with the example file, aiming for the valid numbers—test your skills now!