-
Notifications
You must be signed in to change notification settings - Fork 1.5k
/
WildCardMatcher.cs
97 lines (87 loc) · 3.55 KB
/
WildCardMatcher.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
using System;
namespace Algorithms.Strings.PatternMatching;
/// <summary>
/// Implentation of regular expression matching with support for '.' and '*'.
/// '.' Matches any single character.
/// '*' Matches zero or more of the preceding element.
/// The matching should cover the entire input string (not partial).
/// </summary>
public static class WildCardMatcher
{
/// <summary>
/// Using bottom-up dynamic programming for matching the input string with the pattern.
///
/// Time complexity: O(n*m), where n is the length of the input string and m is the length of the pattern.
///
/// Constrain: The pattern cannot start with '*'.
/// </summary>
/// <param name="inputString">The input string to match.</param>
/// <param name="pattern">The pattern to match.</param>
/// <returns>True if the input string matches the pattern, false otherwise.</returns>
/// <exception cref="ArgumentException">Thrown when the pattern starts with '*'.</exception>
public static bool MatchPattern(string inputString, string pattern)
{
if (pattern.Length > 0 && pattern[0] == '*')
{
throw new ArgumentException("Pattern cannot start with *");
}
var inputLength = inputString.Length + 1;
var patternLength = pattern.Length + 1;
// DP 2d matrix, where dp[i, j] is true if the first i characters in the input string match the first j characters in the pattern
// This DP is initialized to all falses, as it is the default value for a boolean.
var dp = new bool[inputLength, patternLength];
// Empty string and empty pattern are a match
dp[0, 0] = true;
// Since the empty string can only match a pattern that has a * in it, we need to initialize the first row of the DP matrix
for (var j = 1; j < patternLength; j++)
{
if (pattern[j - 1] == '*')
{
dp[0, j] = dp[0, j - 2];
}
}
// Now using bottom-up approach to find for all remaining lenghts of input and pattern
for (var i = 1; i < inputLength; i++)
{
for (var j = 1; j < patternLength; j++)
{
MatchRemainingLenghts(inputString, pattern, dp, i, j);
}
}
return dp[inputLength - 1, patternLength - 1];
}
// Helper method to match the remaining lengths of the input string and the pattern
// This method is called for all i and j where i > 0 and j > 0
private static void MatchRemainingLenghts(string inputString, string pattern, bool[,] dp, int i, int j)
{
// If the characters match or the pattern has a ., then the result is the same as the previous positions.
if (inputString[i - 1] == pattern[j - 1] || pattern[j - 1] == '.')
{
dp[i, j] = dp[i - 1, j - 1];
}
else if (pattern[j - 1] == '*')
{
MatchForZeroOrMore(inputString, pattern, dp, i, j);
}
else
{
// If the characters do not match, then the result is false, which is the default value.
}
}
// Helper method to match for the "*" pattern.
private static void MatchForZeroOrMore(string inputString, string pattern, bool[,] dp, int i, int j)
{
if (dp[i, j - 2])
{
dp[i, j] = true;
}
else if (inputString[i - 1] == pattern[j - 2] || pattern[j - 2] == '.')
{
dp[i, j] = dp[i - 1, j];
}
else
{
// Leave the default value of false
}
}
}