2023-08-14 05:37:41 +00:00
|
|
|
"""
|
|
|
|
Regex matching check if a text matches pattern or not.
|
|
|
|
Pattern:
|
2024-12-30 11:52:03 +00:00
|
|
|
|
|
|
|
1. ``.`` Matches any single character.
|
|
|
|
2. ``*`` Matches zero or more of the preceding element.
|
|
|
|
|
2023-08-14 05:37:41 +00:00
|
|
|
More info:
|
|
|
|
https://medium.com/trick-the-interviwer/regular-expression-matching-9972eb74c03
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
def recursive_match(text: str, pattern: str) -> bool:
|
2024-12-30 11:52:03 +00:00
|
|
|
r"""
|
2023-08-14 05:37:41 +00:00
|
|
|
Recursive matching algorithm.
|
|
|
|
|
2024-12-30 11:52:03 +00:00
|
|
|
| Time complexity: O(2^(\|text\| + \|pattern\|))
|
|
|
|
| Space complexity: Recursion depth is O(\|text\| + \|pattern\|).
|
2023-08-14 05:37:41 +00:00
|
|
|
|
|
|
|
:param text: Text to match.
|
|
|
|
:param pattern: Pattern to match.
|
2024-12-30 11:52:03 +00:00
|
|
|
:return: ``True`` if `text` matches `pattern`, ``False`` otherwise.
|
2023-08-14 05:37:41 +00:00
|
|
|
|
|
|
|
>>> recursive_match('abc', 'a.c')
|
|
|
|
True
|
|
|
|
>>> recursive_match('abc', 'af*.c')
|
|
|
|
True
|
|
|
|
>>> recursive_match('abc', 'a.c*')
|
|
|
|
True
|
|
|
|
>>> recursive_match('abc', 'a.c*d')
|
|
|
|
False
|
|
|
|
>>> recursive_match('aa', '.*')
|
|
|
|
True
|
|
|
|
"""
|
|
|
|
if not pattern:
|
|
|
|
return not text
|
|
|
|
|
|
|
|
if not text:
|
|
|
|
return pattern[-1] == "*" and recursive_match(text, pattern[:-2])
|
|
|
|
|
|
|
|
if text[-1] == pattern[-1] or pattern[-1] == ".":
|
|
|
|
return recursive_match(text[:-1], pattern[:-1])
|
|
|
|
|
|
|
|
if pattern[-1] == "*":
|
|
|
|
return recursive_match(text[:-1], pattern) or recursive_match(
|
|
|
|
text, pattern[:-2]
|
|
|
|
)
|
|
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
def dp_match(text: str, pattern: str) -> bool:
|
2024-12-30 11:52:03 +00:00
|
|
|
r"""
|
2023-08-14 05:37:41 +00:00
|
|
|
Dynamic programming matching algorithm.
|
|
|
|
|
2024-12-30 11:52:03 +00:00
|
|
|
| Time complexity: O(\|text\| * \|pattern\|)
|
|
|
|
| Space complexity: O(\|text\| * \|pattern\|)
|
2023-08-14 05:37:41 +00:00
|
|
|
|
|
|
|
:param text: Text to match.
|
|
|
|
:param pattern: Pattern to match.
|
2024-12-30 11:52:03 +00:00
|
|
|
:return: ``True`` if `text` matches `pattern`, ``False`` otherwise.
|
2023-08-14 05:37:41 +00:00
|
|
|
|
|
|
|
>>> dp_match('abc', 'a.c')
|
|
|
|
True
|
|
|
|
>>> dp_match('abc', 'af*.c')
|
|
|
|
True
|
|
|
|
>>> dp_match('abc', 'a.c*')
|
|
|
|
True
|
|
|
|
>>> dp_match('abc', 'a.c*d')
|
|
|
|
False
|
|
|
|
>>> dp_match('aa', '.*')
|
|
|
|
True
|
|
|
|
"""
|
|
|
|
m = len(text)
|
|
|
|
n = len(pattern)
|
|
|
|
dp = [[False for _ in range(n + 1)] for _ in range(m + 1)]
|
|
|
|
dp[0][0] = True
|
|
|
|
|
|
|
|
for j in range(1, n + 1):
|
|
|
|
dp[0][j] = pattern[j - 1] == "*" and dp[0][j - 2]
|
|
|
|
|
|
|
|
for i in range(1, m + 1):
|
|
|
|
for j in range(1, n + 1):
|
|
|
|
if pattern[j - 1] in {".", text[i - 1]}:
|
|
|
|
dp[i][j] = dp[i - 1][j - 1]
|
|
|
|
elif pattern[j - 1] == "*":
|
|
|
|
dp[i][j] = dp[i][j - 2]
|
|
|
|
if pattern[j - 2] in {".", text[i - 1]}:
|
|
|
|
dp[i][j] |= dp[i - 1][j]
|
|
|
|
else:
|
|
|
|
dp[i][j] = False
|
|
|
|
|
|
|
|
return dp[m][n]
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
import doctest
|
|
|
|
|
|
|
|
doctest.testmod()
|