Created 2025/01/10 at 07:03PM
Last Modified 2025/01/15 at 06:24PM
This is a fun puzzle that was dropped in one of the discord servers, I'm part of (thanks to Anthony - do checkout their channel). This puzzle revolved around regular expressions (regex).
Regex is widely used in programming, data processing, and system administration for tasks like:
But this was a puzzle which required use of regex to do something not so trivial but a lot of fun
Given two positive integers 'a' and 'b', one as an integer, and one as a string, use regex to find if the 'a' is greater than 'b'. So, we have to generate a regex pattern from
a
which when matched tob
should give a match ofb
if the comparison would've return true otherwise it shouldn't match. Note thatb
can be provided with padded 0s, example - b = "023"
Before we move ahead with the solution, take some time and try to think about it.
So, let's take an example. Let's say we have a = 12
. So, we know that for something to be greater than a
, it can have
So, we have 3 conditions here, let's try to formulate each into it's own regex pattern.
r"^1[3-9]$"
r"^[2-9]\d$"
r"^[1-9]\d{2,}$"
So, our final regex would be r"^1[3-9]$|^[2-9]\d$|^[1-9]\d{2,}$"
. Let's try this with some values of "b".
$ python
>>> import re
>>> p = r"^1[3-9]$|^[2-9]\d$|^[1-9]\d{2,}$"
>>> re.match(p, "2") is not None
False
>>> re.match(p, "15") is not None
True
>>> re.match(p, "33145") is not None
True
Great! But we need to auto-generate this regex based on value of a, and it can be quite large. We have to do above exercise logically for each digit position. If a
had 3 digits, then for something to be greater than a
, it can
We do have a edge case here when we enounter a 9, example - a = 291. For something to be greater than a, we can generate regex patterns based on above conditions
r"^29[2-9]$"
r"^[3-9]\d{2}$"
r"^[1-9]\d{3}$"
So for ??
, you'll notice that what we have to do is bump up the digit to the left of 9 by 1, and replace 9 with 0, and 3rd digit can be anything. So, it becomes a subset of r"^[3-9]\d{2}$"
.
>>> import re
>>> def gt(n: int) -> str:
digits = list(map(int, str(n)))
s = ""
num_digits = len(digits)
for i, digit in enumerate(digits):
prefix = "".join(map(str, digits[:i]))
if digit == 9:
continue
else:
s += f"^{prefix}[{digit + 1}-9]"
if num_digits - i - 1 > 0:
s += f"[0-9]{{{num_digits - i - 1},}}"
s += "$|"
s += f"^[1-9][0-9]{{{num_digits},}}$|"
return s[:-1]
>>> gt(291)
'^[3-9][0-9]{2,}$|^29[2-9]$|^[1-9][0-9]{3,}$'
>>> p = re.compile(gt(291))
>>> for b in range(1, 1000000):
if b <= 291:
assert re.match(p, str(b)) is None
else:
assert re.match(p, str(b)).string == str(b)
Here's an exercise for you. Try to implement a lt
method which mimics the "less than" functionality.
def lt(n: int) -> str: if n == 0: return "^$" digits = list(map(int, str(n))) s = "" num_digits = len(digits) for i, digit in enumerate(digits): prefix = "".join(map(str, digits[:i])) if digit == 0: if i > 0: prev_digit = digits[i - 1] if prev_digit > 0: prev_digit -= 1 else: prev_digit = 9 s += f"^{prefix[:-1]}{prev_digit}[0-9]{{0,{num_digits - i - 2}}}$|" else: s += f"^{prefix}[0-{digit - 1}]" if num_digits - i - 1 > 0: s += f"[0-9]{{0,{num_digits - i - 1}}}" s += "$|" if num_digits > 2: s += f"^[1-9][0-9]{{0,{num_digits - 2}}}$|" return s[:-1]
Hope you enjoyed it as much as I did!