Below is the implementation of the wildcard pattern matcher:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164/** * Determine if a text string matches a given pattern with support for '?' and '*'. * Dynamic Programming approach. * * @param {string} text - The input text string * @param {string} pattern - The pattern to match against * @return {boolean} - True if the text matches the pattern, false otherwise */function isMatchDP(text, pattern) { const m = text.length; const n = pattern.length; // Create a DP table const dp = Array(m + 1).fill().map(() => Array(n + 1).fill(false)); // Empty pattern matches empty text dp[0][0] = true; // Handle patterns starting with '*' for (let j = 1; j <= n; j++) { if (pattern[j - 1] === '*') { dp[0][j] = dp[0][j - 1]; } } // Fill the DP table for (let i = 1; i <= m; i++) { for (let j = 1; j <= n; j++) { if (pattern[j - 1] === '?') { // '?' matches any single character dp[i][j] = dp[i - 1][j - 1]; } else if (pattern[j - 1] === '*') { // '*' can match zero or more characters dp[i][j] = dp[i][j - 1] || dp[i - 1][j]; } else { // Regular character must match dp[i][j] = (text[i - 1] === pattern[j - 1]) && dp[i - 1][j - 1]; } } } return dp[m][n];} /** * Determine if a text string matches a given pattern with support for '?' and '*'. * Greedy approach with backtracking. * * @param {string} text - The input text string * @param {string} pattern - The pattern to match against * @return {boolean} - True if the text matches the pattern, false otherwise */function isMatchGreedy(text, pattern) { const m = text.length; const n = pattern.length; let i = 0; // pointer for text let j = 0; // pointer for pattern let starIdx = -1; // position of last '*' in pattern let match = 0; // position in text corresponding to last '*' while (i < m) { // If current characters match or pattern has '?' if (j < n && (pattern[j] === '?' || pattern[j] === text[i])) { i++; j++; } // If pattern has '*' else if (j < n && pattern[j] === '*') { starIdx = j; match = i; j++; } // If we've seen a '*' before, backtrack else if (starIdx !== -1) { j = starIdx + 1; match++; i = match; } // If none of the above conditions are met, no match else { return false; } } // Skip any remaining '*' characters in pattern while (j < n && pattern[j] === '*') { j++; } // Return true if we've reached the end of the pattern return j === n;} /** * Determine if a text string matches a given pattern with support for '?' and '*'. * Recursive approach with memoization. * * @param {string} text - The input text string * @param {string} pattern - The pattern to match against * @return {boolean} - True if the text matches the pattern, false otherwise */function isMatchRecursive(text, pattern) { // Create a memoization map const memo = new Map(); function match(i, j) { // Create a unique key for the current state const key = i + ',' + j; // Check if we've already computed this result if (memo.has(key)) { return memo.get(key); } // Base case: if we've reached the end of the pattern if (j === pattern.length) { return i === text.length; } // Base case: if we've reached the end of the text if (i === text.length) { // Remaining pattern must be all '*' for (let k = j; k < pattern.length; k++) { if (pattern[k] !== '*') { return false; } } return true; } let result; // Current character in pattern if (pattern[j] === '?') { // '?' matches any single character result = match(i + 1, j + 1); } else if (pattern[j] === '*') { // '*' can match zero or more characters result = match(i, j + 1) || match(i + 1, j); } else { // Regular character must match result = (text[i] === pattern[j]) && match(i + 1, j + 1); } // Store the result in the memoization map memo.set(key, result); return result; } return match(0, 0);} // Test casesconsole.log(isMatchDP("report.pdf", "*.pdf")); // trueconsole.log(isMatchDP("document2023.docx", "document????.docx")); // trueconsole.log(isMatchDP("image.png", "*.jpg")); // false console.log(isMatchGreedy("report.pdf", "*.pdf")); // trueconsole.log(isMatchGreedy("document2023.docx", "document????.docx")); // trueconsole.log(isMatchGreedy("image.png", "*.jpg")); // false console.log(isMatchRecursive("report.pdf", "*.pdf")); // trueconsole.log(isMatchRecursive("document2023.docx", "document????.docx")); // trueconsole.log(isMatchRecursive("image.png", "*.jpg")); // false
Let's break down the implementation:
Implement the wildcard pattern matcher solution in different programming languages.
Below is the implementation of the wildcard pattern matcher in different programming languages. Select a language tab to view the corresponding code.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164/** * Determine if a text string matches a given pattern with support for '?' and '*'. * Dynamic Programming approach. * * @param {string} text - The input text string * @param {string} pattern - The pattern to match against * @return {boolean} - True if the text matches the pattern, false otherwise */function isMatchDP(text, pattern) { const m = text.length; const n = pattern.length; // Create a DP table const dp = Array(m + 1).fill().map(() => Array(n + 1).fill(false)); // Empty pattern matches empty text dp[0][0] = true; // Handle patterns starting with '*' for (let j = 1; j <= n; j++) { if (pattern[j - 1] === '*') { dp[0][j] = dp[0][j - 1]; } } // Fill the DP table for (let i = 1; i <= m; i++) { for (let j = 1; j <= n; j++) { if (pattern[j - 1] === '?') { // '?' matches any single character dp[i][j] = dp[i - 1][j - 1]; } else if (pattern[j - 1] === '*') { // '*' can match zero or more characters dp[i][j] = dp[i][j - 1] || dp[i - 1][j]; } else { // Regular character must match dp[i][j] = (text[i - 1] === pattern[j - 1]) && dp[i - 1][j - 1]; } } } return dp[m][n];} /** * Determine if a text string matches a given pattern with support for '?' and '*'. * Greedy approach with backtracking. * * @param {string} text - The input text string * @param {string} pattern - The pattern to match against * @return {boolean} - True if the text matches the pattern, false otherwise */function isMatchGreedy(text, pattern) { const m = text.length; const n = pattern.length; let i = 0; // pointer for text let j = 0; // pointer for pattern let starIdx = -1; // position of last '*' in pattern let match = 0; // position in text corresponding to last '*' while (i < m) { // If current characters match or pattern has '?' if (j < n && (pattern[j] === '?' || pattern[j] === text[i])) { i++; j++; } // If pattern has '*' else if (j < n && pattern[j] === '*') { starIdx = j; match = i; j++; } // If we've seen a '*' before, backtrack else if (starIdx !== -1) { j = starIdx + 1; match++; i = match; } // If none of the above conditions are met, no match else { return false; } } // Skip any remaining '*' characters in pattern while (j < n && pattern[j] === '*') { j++; } // Return true if we've reached the end of the pattern return j === n;} /** * Determine if a text string matches a given pattern with support for '?' and '*'. * Recursive approach with memoization. * * @param {string} text - The input text string * @param {string} pattern - The pattern to match against * @return {boolean} - True if the text matches the pattern, false otherwise */function isMatchRecursive(text, pattern) { // Create a memoization map const memo = new Map(); function match(i, j) { // Create a unique key for the current state const key = i + ',' + j; // Check if we've already computed this result if (memo.has(key)) { return memo.get(key); } // Base case: if we've reached the end of the pattern if (j === pattern.length) { return i === text.length; } // Base case: if we've reached the end of the text if (i === text.length) { // Remaining pattern must be all '*' for (let k = j; k < pattern.length; k++) { if (pattern[k] !== '*') { return false; } } return true; } let result; // Current character in pattern if (pattern[j] === '?') { // '?' matches any single character result = match(i + 1, j + 1); } else if (pattern[j] === '*') { // '*' can match zero or more characters result = match(i, j + 1) || match(i + 1, j); } else { // Regular character must match result = (text[i] === pattern[j]) && match(i + 1, j + 1); } // Store the result in the memoization map memo.set(key, result); return result; } return match(0, 0);} // Test casesconsole.log(isMatchDP("report.pdf", "*.pdf")); // trueconsole.log(isMatchDP("document2023.docx", "document????.docx")); // trueconsole.log(isMatchDP("image.png", "*.jpg")); // false console.log(isMatchGreedy("report.pdf", "*.pdf")); // trueconsole.log(isMatchGreedy("document2023.docx", "document????.docx")); // trueconsole.log(isMatchGreedy("image.png", "*.jpg")); // false console.log(isMatchRecursive("report.pdf", "*.pdf")); // trueconsole.log(isMatchRecursive("document2023.docx", "document????.docx")); // trueconsole.log(isMatchRecursive("image.png", "*.jpg")); // false
First, understand that we need to implement a wildcard pattern matcher that supports '?' (matches any single character) and '*' (matches any sequence of characters).
Identify the base cases: if we've reached the end of the pattern, we should have also reached the end of the text for a match.
Handle the special characters '?' and '*' separately. The '*' character is particularly tricky as it can match zero or more characters.
Implement a bottom-up dynamic programming solution using a 2D table.
Implement a greedy solution with backtracking for better space efficiency.
Implement a recursive solution with memoization as an alternative approach.
Modify the code to implement an alternative approach and test it with the same examples.
Implement a function that solves the wildcard pattern matcher problem using a different approach than shown above.
Handle the case where either the text or pattern is empty.
Handle patterns that consist only of '*' characters, which can match any text.
Handle complex patterns with multiple '*' and '?' characters.
Below is the implementation of the wildcard pattern matcher:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164/** * Determine if a text string matches a given pattern with support for '?' and '*'. * Dynamic Programming approach. * * @param {string} text - The input text string * @param {string} pattern - The pattern to match against * @return {boolean} - True if the text matches the pattern, false otherwise */function isMatchDP(text, pattern) { const m = text.length; const n = pattern.length; // Create a DP table const dp = Array(m + 1).fill().map(() => Array(n + 1).fill(false)); // Empty pattern matches empty text dp[0][0] = true; // Handle patterns starting with '*' for (let j = 1; j <= n; j++) { if (pattern[j - 1] === '*') { dp[0][j] = dp[0][j - 1]; } } // Fill the DP table for (let i = 1; i <= m; i++) { for (let j = 1; j <= n; j++) { if (pattern[j - 1] === '?') { // '?' matches any single character dp[i][j] = dp[i - 1][j - 1]; } else if (pattern[j - 1] === '*') { // '*' can match zero or more characters dp[i][j] = dp[i][j - 1] || dp[i - 1][j]; } else { // Regular character must match dp[i][j] = (text[i - 1] === pattern[j - 1]) && dp[i - 1][j - 1]; } } } return dp[m][n];} /** * Determine if a text string matches a given pattern with support for '?' and '*'. * Greedy approach with backtracking. * * @param {string} text - The input text string * @param {string} pattern - The pattern to match against * @return {boolean} - True if the text matches the pattern, false otherwise */function isMatchGreedy(text, pattern) { const m = text.length; const n = pattern.length; let i = 0; // pointer for text let j = 0; // pointer for pattern let starIdx = -1; // position of last '*' in pattern let match = 0; // position in text corresponding to last '*' while (i < m) { // If current characters match or pattern has '?' if (j < n && (pattern[j] === '?' || pattern[j] === text[i])) { i++; j++; } // If pattern has '*' else if (j < n && pattern[j] === '*') { starIdx = j; match = i; j++; } // If we've seen a '*' before, backtrack else if (starIdx !== -1) { j = starIdx + 1; match++; i = match; } // If none of the above conditions are met, no match else { return false; } } // Skip any remaining '*' characters in pattern while (j < n && pattern[j] === '*') { j++; } // Return true if we've reached the end of the pattern return j === n;} /** * Determine if a text string matches a given pattern with support for '?' and '*'. * Recursive approach with memoization. * * @param {string} text - The input text string * @param {string} pattern - The pattern to match against * @return {boolean} - True if the text matches the pattern, false otherwise */function isMatchRecursive(text, pattern) { // Create a memoization map const memo = new Map(); function match(i, j) { // Create a unique key for the current state const key = i + ',' + j; // Check if we've already computed this result if (memo.has(key)) { return memo.get(key); } // Base case: if we've reached the end of the pattern if (j === pattern.length) { return i === text.length; } // Base case: if we've reached the end of the text if (i === text.length) { // Remaining pattern must be all '*' for (let k = j; k < pattern.length; k++) { if (pattern[k] !== '*') { return false; } } return true; } let result; // Current character in pattern if (pattern[j] === '?') { // '?' matches any single character result = match(i + 1, j + 1); } else if (pattern[j] === '*') { // '*' can match zero or more characters result = match(i, j + 1) || match(i + 1, j); } else { // Regular character must match result = (text[i] === pattern[j]) && match(i + 1, j + 1); } // Store the result in the memoization map memo.set(key, result); return result; } return match(0, 0);} // Test casesconsole.log(isMatchDP("report.pdf", "*.pdf")); // trueconsole.log(isMatchDP("document2023.docx", "document????.docx")); // trueconsole.log(isMatchDP("image.png", "*.jpg")); // false console.log(isMatchGreedy("report.pdf", "*.pdf")); // trueconsole.log(isMatchGreedy("document2023.docx", "document????.docx")); // trueconsole.log(isMatchGreedy("image.png", "*.jpg")); // false console.log(isMatchRecursive("report.pdf", "*.pdf")); // trueconsole.log(isMatchRecursive("document2023.docx", "document????.docx")); // trueconsole.log(isMatchRecursive("image.png", "*.jpg")); // false
Let's break down the implementation:
Implement the wildcard pattern matcher solution in different programming languages.
Below is the implementation of the wildcard pattern matcher in different programming languages. Select a language tab to view the corresponding code.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164/** * Determine if a text string matches a given pattern with support for '?' and '*'. * Dynamic Programming approach. * * @param {string} text - The input text string * @param {string} pattern - The pattern to match against * @return {boolean} - True if the text matches the pattern, false otherwise */function isMatchDP(text, pattern) { const m = text.length; const n = pattern.length; // Create a DP table const dp = Array(m + 1).fill().map(() => Array(n + 1).fill(false)); // Empty pattern matches empty text dp[0][0] = true; // Handle patterns starting with '*' for (let j = 1; j <= n; j++) { if (pattern[j - 1] === '*') { dp[0][j] = dp[0][j - 1]; } } // Fill the DP table for (let i = 1; i <= m; i++) { for (let j = 1; j <= n; j++) { if (pattern[j - 1] === '?') { // '?' matches any single character dp[i][j] = dp[i - 1][j - 1]; } else if (pattern[j - 1] === '*') { // '*' can match zero or more characters dp[i][j] = dp[i][j - 1] || dp[i - 1][j]; } else { // Regular character must match dp[i][j] = (text[i - 1] === pattern[j - 1]) && dp[i - 1][j - 1]; } } } return dp[m][n];} /** * Determine if a text string matches a given pattern with support for '?' and '*'. * Greedy approach with backtracking. * * @param {string} text - The input text string * @param {string} pattern - The pattern to match against * @return {boolean} - True if the text matches the pattern, false otherwise */function isMatchGreedy(text, pattern) { const m = text.length; const n = pattern.length; let i = 0; // pointer for text let j = 0; // pointer for pattern let starIdx = -1; // position of last '*' in pattern let match = 0; // position in text corresponding to last '*' while (i < m) { // If current characters match or pattern has '?' if (j < n && (pattern[j] === '?' || pattern[j] === text[i])) { i++; j++; } // If pattern has '*' else if (j < n && pattern[j] === '*') { starIdx = j; match = i; j++; } // If we've seen a '*' before, backtrack else if (starIdx !== -1) { j = starIdx + 1; match++; i = match; } // If none of the above conditions are met, no match else { return false; } } // Skip any remaining '*' characters in pattern while (j < n && pattern[j] === '*') { j++; } // Return true if we've reached the end of the pattern return j === n;} /** * Determine if a text string matches a given pattern with support for '?' and '*'. * Recursive approach with memoization. * * @param {string} text - The input text string * @param {string} pattern - The pattern to match against * @return {boolean} - True if the text matches the pattern, false otherwise */function isMatchRecursive(text, pattern) { // Create a memoization map const memo = new Map(); function match(i, j) { // Create a unique key for the current state const key = i + ',' + j; // Check if we've already computed this result if (memo.has(key)) { return memo.get(key); } // Base case: if we've reached the end of the pattern if (j === pattern.length) { return i === text.length; } // Base case: if we've reached the end of the text if (i === text.length) { // Remaining pattern must be all '*' for (let k = j; k < pattern.length; k++) { if (pattern[k] !== '*') { return false; } } return true; } let result; // Current character in pattern if (pattern[j] === '?') { // '?' matches any single character result = match(i + 1, j + 1); } else if (pattern[j] === '*') { // '*' can match zero or more characters result = match(i, j + 1) || match(i + 1, j); } else { // Regular character must match result = (text[i] === pattern[j]) && match(i + 1, j + 1); } // Store the result in the memoization map memo.set(key, result); return result; } return match(0, 0);} // Test casesconsole.log(isMatchDP("report.pdf", "*.pdf")); // trueconsole.log(isMatchDP("document2023.docx", "document????.docx")); // trueconsole.log(isMatchDP("image.png", "*.jpg")); // false console.log(isMatchGreedy("report.pdf", "*.pdf")); // trueconsole.log(isMatchGreedy("document2023.docx", "document????.docx")); // trueconsole.log(isMatchGreedy("image.png", "*.jpg")); // false console.log(isMatchRecursive("report.pdf", "*.pdf")); // trueconsole.log(isMatchRecursive("document2023.docx", "document????.docx")); // trueconsole.log(isMatchRecursive("image.png", "*.jpg")); // false
First, understand that we need to implement a wildcard pattern matcher that supports '?' (matches any single character) and '*' (matches any sequence of characters).
Identify the base cases: if we've reached the end of the pattern, we should have also reached the end of the text for a match.
Handle the special characters '?' and '*' separately. The '*' character is particularly tricky as it can match zero or more characters.
Implement a bottom-up dynamic programming solution using a 2D table.
Implement a greedy solution with backtracking for better space efficiency.
Implement a recursive solution with memoization as an alternative approach.
Modify the code to implement an alternative approach and test it with the same examples.
Implement a function that solves the wildcard pattern matcher problem using a different approach than shown above.
Handle the case where either the text or pattern is empty.
Handle patterns that consist only of '*' characters, which can match any text.
Handle complex patterns with multiple '*' and '?' characters.