Wildcard Matching is a pattern matching algorithm that checks whether a given input string matches a specified pattern that may contain wildcards. Wildcards are special characters that can represent one or more characters in a string. In most wildcard matching problems, the two most common wildcard characters are:
*
(asterisk): Matches zero or more characters.?
(question mark): Matches exactly one character.
The problem of Wildcard Matching is widely used in file systems for searching files by name and in string processing applications. The goal is to determine if a string (referred to as the input string) matches a pattern that contains these special wildcard characters.
Input-Output Examples
Example 1:
Input: s = "abcdef", p = "a*d*f"
Output: True
Explanation: The * wildcard matches "bcde", a
nd the string "abcdef" matches the pattern "a*d*f".
Example 2:
Input: s = "abcdef", p = "a*c?f"
Output: False
Explanation: The ? wildcard matches exactly
one character, but "c?f" does not match "bcdef".
How Wildcard Matching Works ?
The Wildcard Matching problem is typically solved using dynamic programming or backtracking. In both approaches, the goal is to match each character of the input string with the corresponding character in the pattern, while appropriately handling the wildcard characters.
Step-by-step explanation:
- Exact Match: If the current character in the pattern is a regular character (neither
*
nor?
), it must exactly match the corresponding character in the input string. - Single Character Match (
**?**
): If the pattern contains?
, it can match exactly one character in the input string. - Zero or More Characters Match (
*****
): If the pattern contains*
, it can match zero or more characters in the input string. This means the*
can be treated as an empty sequence, or it can match one or more characters.
Algorithm
Here’s a general approach to solve Wildcard Matching using dynamic programming:
- Create a 2D boolean table where
dp[i][j]
isTrue
if the firsti
characters of the input string match the firstj
characters of the pattern. - Base Case:
dp[0][0]
isTrue
because an empty pattern matches an empty string. - Fill the DP Table:
- If the current character in the pattern is a regular character or
?
, setdp[i][j]
based on whetherdp[i-1][j-1]
isTrue
. - If the current character in the pattern is
*
, it can match zero or more characters. Thus, setdp[i][j]
toTrue
if eitherdp[i][j-1]
(matching zero characters) ordp[i-1][j]
(matching one or more characters) isTrue
.
- If the current character in the pattern is a regular character or
- Result: The final result will be stored in
dp[n][m]
wheren
is the length of the input string andm
is the length of the pattern.
Example of Wildcard Matching
Let’s take an example where the input string is "abefcdgiescdfimde"
and the pattern is "ab*cd?i*de"
:
- Input String:
"abefcdgiescdfimde"
- Pattern:
"ab*cd?i*de"
Explanation:
- The pattern starts with
"ab"
which exactly matches the beginning of the input string. - The
*
wildcard matches any sequence of characters, so it can match"ef"
. - The next characters
"cd"
match"cd"
in the input string. - The
?
wildcard matches exactly one character, which matches"g"
. - The characters
"i"
match in both the pattern and input string. - The second
*
wildcard can match"escdfim"
. - The final
"de"
in the pattern matches"de"
in the input string.
Therefore, the input string matches the pattern.
C++ Program to implement Wildcard Matching Pattern
#include <iostream>
#include <vector>
using namespace std;
// Function to check if the input string matches the pattern
bool isMatch(string s, string p) {
int n = s.length();
int m = p.length();
// Create a 2D DP table with (n+1)x(m+1) dimensions
vector<vector<bool>> dp(n + 1, vector<bool>(m + 1, false));
// Base case: Empty pattern matches empty string
dp[0][0] = true;
// Fill the first row (when the string is empty)
for (int j = 1; j <= m; j++) {
if (p[j - 1] == '*') {
dp[0][j] = dp[0][j - 1];
}
}
// Fill the DP table
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (p[j - 1] == '?' || p[j - 1] == s[i - 1]) {
dp[i][j] = dp[i - 1][j - 1]; // Match one character
} else if (p[j - 1] == '*') {
dp[i][j] = dp[i - 1][j] || dp[i][j - 1]; // Match zero or more characters
}
}
}
return dp[n][m];
}
// Main function
int main() {
string s = "abefcdgiescdfimde";
string p = "ab*cd?i*de";
if (isMatch(s, p)) {
cout << "The pattern matches the string." << endl;
} else {
cout << "The pattern does not match the string." << endl;
}
return 0;
}
Java Program to implement Wildcard Matching Pattern
public class Main {
// Function to check if the input string matches the pattern
public static boolean isMatch(String s, String p) {
int n = s.length();
int m = p.length();
// Create a 2D DP table with (n+1)x(m+1) dimensions
boolean[][] dp = new boolean[n + 1][m + 1];
// Base case: Empty pattern matches empty string
dp[0][0] = true;
// Fill the first row (when the string is empty)
for (int j = 1; j <= m; j++) {
if (p.charAt(j - 1) == '*') {
dp[0][j] = dp[0][j - 1];
}
}
// Fill the DP table
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (p.charAt(j - 1) == '?' || p.charAt(j - 1) == s.charAt(i - 1)) {
dp[i][j] = dp[i - 1][j - 1]; // Match one character
} else if (p.charAt(j - 1) == '*') {
dp[i][j] = dp[i - 1][j] || dp[i][j - 1]; // Match zero or more characters
}
}
}
return dp[n][m];
}
// Main function
public static void main(String[] args) {
String s = "abefcdgiescdfimde";
String p = "ab*cd?i*de";
if (isMatch(s, p)) {
System.out.println("The pattern matches the string.");
} else {
System.out.println("The pattern does not match the string.");
}
}
}
Python Program to implement Wildcard Matching Pattern
# Function to check if the input string matches the pattern
def is_match(s, p):
n = len(s)
m = len(p)
# Create a 2D DP table with (n+1)x(m+1) dimensions
dp = [[False] * (m + 1) for _ in range(n + 1)]
# Base case: Empty pattern matches empty string
dp[0][0] = True
# Fill the first row (when the string is empty)
for j in range(1, m + 1):
if p[j - 1] == '*':
dp[0][j] = dp[0][j - 1]
# Fill the DP table
for i in range(1, n + 1):
for j in range(1, m + 1):
if p[j - 1] == '?' or p[j - 1] == s[i - 1]:
dp[i][j] = dp[i - 1][j - 1] # Match one character
elif p[j - 1] == '*':
dp[i][j] = dp[i - 1][j] or dp[i][j - 1] # Match zero or more characters
return dp[n][m]
# Main function
if __name__ == "__main__":
s = "abefcdgiescdfimde"
p = "ab*cd?i*de"
if is_match(s, p):
print("The pattern matches the string.")
else:
print("The pattern does not match the string.")
Properties of Wildcard Matching
- Time Complexity: The time complexity of the dynamic programming approach is O(n * m), where
n
is the length of the input string andm
is the length of the pattern. - Space Complexity: The space complexity is also O(n * m) due to the 2D DP table.
When to Use Wildcard Matching ?
Wildcard Matching is useful in scenarios like:
- File searching: When searching for files in a directory, wildcards like
*
and?
can be used to specify flexible search patterns (e.g.,*.txt
to match all text files). - String pattern matching: When dealing with strings that need flexible matching criteria, such as partial string matching or searching for substrings with unknown lengths.