Capture groups in Ruby - Time & Space Complexity
When using capture groups in Ruby regular expressions, it is important to understand how the time to find matches grows as the input string gets longer.
We want to know how the work done by the regex engine changes with bigger inputs.
Analyze the time complexity of the following code snippet.
text = "abc123def456"
pattern = /(\d{3})/ # capture groups for 3 digits
matches = text.scan(pattern)
This code finds all groups of three digits in the text and stores them.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: The regex engine scans through the input string character by character to find matches.
- How many times: It checks each position in the string once, trying to match the pattern with capture groups.
As the input string gets longer, the regex engine does more checks, roughly one per character.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 10 checks |
| 100 | About 100 checks |
| 1000 | About 1000 checks |
Pattern observation: The work grows roughly in direct proportion to the input size.
Time Complexity: O(n)
This means the time to find capture groups grows linearly as the input string gets longer.
[X] Wrong: "Capture groups make the regex run much slower, like exponentially slower."
[OK] Correct: Simple capture groups add only a small fixed amount of work per match attempt, so the overall time still grows linearly with input size.
Understanding how capture groups affect regex performance helps you write efficient pattern matching code, a useful skill in many programming tasks.
"What if the pattern used nested capture groups or backreferences? How would the time complexity change?"
Practice
/(\d{3})-(\d{2})/?Solution
Step 1: Understand capture groups in regex
Capture groups are parts of a pattern enclosed in parentheses that save matched text.Step 2: Analyze the example pattern
The pattern/(\d{3})-(\d{2})/has two capture groups for three digits and two digits separated by a dash.Final Answer:
It saves parts of the matched text for later use. -> Option BQuick Check:
Capture groups = save matched parts [OK]
- Thinking capture groups repeat patterns
- Confusing capture groups with non-capturing groups
- Believing capture groups ignore matched text
Solution
Step 1: Identify capture group syntax
Capture groups in Ruby regex are created by enclosing the pattern in parentheses().Step 2: Compare options
/(abc)/ uses parentheses correctly. /[abc]/ uses square brackets which match any one character. /{abc}/ uses curly braces which are quantifiers. /abc/ has no capture group.Final Answer:
/(abc)/ -> Option AQuick Check:
Parentheses = capture group [OK]
- Using square brackets [] instead of parentheses
- Using curly braces {} incorrectly
- Forgetting parentheses for capture groups
text = "Order 123-45"
match = text.match(/(\d{3})-(\d{2})/)
[match[0], match[1], match[2]]Solution
Step 1: Understand match and capture groups
Thematchmethod returns a MatchData object.match[0]is the full matched string,match[1]andmatch[2]are the first and second capture groups.Step 2: Apply to given string and pattern
The pattern matches "123-45" in the text. The first group captures "123", the second "45".Final Answer:
["123-45", "123", "45"] -> Option DQuick Check:
Full match + groups = ["123-45", "123", "45"] [OK]
- Confusing full match with capture groups
- Swapping capture group order
- Ignoring the dash in the match
text = "abc123"
match = text.match(/(\d{3})/)
puts match[1]Solution
Step 1: Check the regex and string
The regex/(\d{3})/looks for exactly three digits. The string "abc123" contains "123" which matches.Step 2: Understand match and output
The match object captures "123" in group 1.puts match[1]prints "123".Final Answer:
It prints "123" correctly. -> Option AQuick Check:
Digits found and printed = "123" [OK]
- Assuming no match causes error
- Expecting nil instead of matched string
- Confusing syntax error with runtime error
"2023-04-01, 2024-12-31", which Ruby code correctly extracts all year parts using capture groups?Solution
Step 1: Understand the goal and methods
We want all years from multiple dates.scanfinds all matches,matchfinds only first.Step 2: Analyze each option
dates.scan(/(\d{4})-\d{2}-\d{2}/).flatten usesscanwith a capture group for year, returning all years as array. dates.match(/(\d{4})-\d{2}-\d{2}/) usesmatchwhich returns only first match. dates.scan(/\d{4}-\d{2}-\d{2}/) usesscanbut no capture group, returns full dates. dates.match(/\d{4}/) usesmatchwithout capture group, only first year.Final Answer:
dates.scan(/(\d{4})-\d{2}-\d{2}/).flatten -> Option CQuick Check:
scan + capture group = all years [OK]
- Using match instead of scan for multiple matches
- Not using capture groups to extract parts
- Forgetting to flatten nested arrays from scan
Ready to prove it?
Pass the test and this pattern moves unknown
