Recursive pattern matching in C Sharp (C#) - Time & Space Complexity
When we use recursive pattern matching, the program calls itself to check parts of data step by step.
We want to know how the time it takes grows as the input gets bigger.
Analyze the time complexity of the following code snippet.
bool MatchPattern(string text, string pattern) {
return (text, pattern) switch {
("", "") => true,
(_, "") => false,
(string t, string p) when p[0] == '*' =>
MatchPattern(t, p[1..]) || (t != "" && MatchPattern(t[1..], p)),
(string t, string p) when p[0] == '?' =>
t != "" && MatchPattern(t[1..], p[1..]),
(string t, string p) =>
t != "" && t[0] == p[0] && MatchPattern(t[1..], p[1..])
};
}
This code checks if a text matches a pattern with wildcards using recursion and pattern matching.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Recursive calls that reduce the text or pattern by one character.
- How many times: Up to the length of the text and pattern combined, but can branch due to '*'.
Each recursive call tries to match smaller parts of the text and pattern, sometimes branching into two calls when '*' is found.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | Up to about 2^10 (around 1000) calls in worst case |
| 100 | Up to about 2^100 calls (very large, impractical) |
| 1000 | Exponential growth, too large to compute |
Pattern observation: The number of operations can grow very fast, doubling with each '*' in the pattern.
Time Complexity: O(2^(m+n))
This means the time can grow exponentially with the combined length of text (m) and pattern (n), especially due to branching on '*'.
[X] Wrong: "The recursion will always run in linear time because it reduces the input each call."
[OK] Correct: Sometimes the recursion splits into two calls (branching), causing the total calls to grow exponentially, not just linearly.
Understanding recursive pattern matching helps you think about how recursion and branching affect performance, a key skill in many coding problems.
"What if we added memoization to store results of subproblems? How would the time complexity change?"
Practice
Solution
Step 1: Understand recursive pattern matching concept
Recursive pattern matching is used to analyze data by breaking it down into smaller parts recursively.Step 2: Identify the purpose from options
To break down complex data structures step-by-step correctly states that it breaks down complex data structures step-by-step, which matches the concept.Final Answer:
To break down complex data structures step-by-step -> Option AQuick Check:
Recursive pattern matching = step-by-step data breakdown [OK]
- Confusing recursion with infinite loops
- Thinking it declares variables
- Assuming it handles exceptions
Solution
Step 1: Recall C# recursive pattern syntax
In C#, recursive patterns use parentheses with variables inside, likeNode(var value, var next).Step 2: Compare options to correct syntax
case Node(var value, var next): matches the correct syntax. Other options use invalid symbols like brackets or arrows.Final Answer:
case Node(var value, var next): -> Option AQuick Check:
Recursive pattern syntax uses parentheses ( ) [OK]
- Using square brackets instead of parentheses
- Using arrows or semicolons incorrectly
- Missing 'var' keyword before variables
record Node(int Value, Node? Next);
Node list = new(1, new(2, new(3, null)));
string PrintList(Node? node) => node switch
{
null => "End",
Node(var v, var n) => $"{v} -> " + PrintList(n)
};
Console.WriteLine(PrintList(list));
Solution
Step 1: Understand the recursive function PrintList
PrintList prints the current node's value, then calls itself on the next node, until it reaches null and returns "End".Step 2: Trace the output for list nodes 1, 2, 3
It prints "1 -> " + PrintList(2), then "2 -> " + PrintList(3), then "3 -> " + PrintList(null), which returns "End".Final Answer:
1 -> 2 -> 3 -> End -> Option CQuick Check:
Recursive print with null base case = 1 -> 2 -> 3 -> End [OK]
- Ignoring the base case output
- Missing arrows in output
- Confusing spaces with arrows
record Node(int Value, Node? Next);
string PrintList(Node? node) => node switch
{
Node(var v, var n) => $"{v} -> " + PrintList(n),
null => "End"
};
Solution
Step 1: Check switch expression order and syntax
In C#, switch expressions check patterns top to bottom. Here, 'Node' pattern matches non-null nodes, and 'null' matches null nodes. The order is valid.Step 2: Verify syntax correctness
The pattern syntax and semicolons are correct. No syntax errors exist.Final Answer:
No error, code works correctly -> Option BQuick Check:
Pattern order and syntax are valid [OK]
- Thinking null must come first
- Assuming missing semicolon error
- Misreading pattern syntax
record TreeNode(int Value, TreeNode? Left, TreeNode? Right);
Write a recursive pattern matching expression to calculate the sum of all values in the tree. Which of the following switch expressions correctly implements this?
Solution
Step 1: Understand the tree structure and sum logic
The tree node has three parts: Value, Left, and Right. The sum should add the current value plus sums of left and right subtrees.Step 2: Check each option for correct pattern and order
node switch { null => 0, TreeNode(var v, var l, var r) => v + Sum(l) + Sum(r) } correctly matches null first (base case) and then TreeNode with three variables (value, left, right). It sums value + Sum(left) + Sum(right). node switch { TreeNode(v, l, r) => v + Sum(l) + Sum(r), null => 0 } misses 'var' keywords, which is required. node switch { null => 0, TreeNode(v, l, r) => v + Sum(l) + Sum(r) } misses 'var' keywords, which is required. node switch { null => 0, TreeNode(var v, var l) => v + Sum(l) } misses the right subtree.Final Answer:
node switch { null => 0, TreeNode(var v, var l, var r) => v + Sum(l) + Sum(r) } -> Option DQuick Check:
Correct pattern with all parts and base case first [OK]
- Omitting 'var' in pattern variables
- Missing one subtree in sum
- Placing null case after non-null without care
Ready to prove it?
Pass the test and this pattern moves unknown
