Skip to main content

Leetcode 10: Regular Expression Matching

1. Problem Statement (Simple Explanation):


You are given:

  • A string s (the text).

  • A pattern p (the regex-like pattern).

You must implement a function:

  • bool isMatch(string s, string p)

with the following rules:

  • '.' matches any single character.

  • '*' matches zero or more of the preceding element (the character just before *).

  • The entire string s must be matched by the pattern p (no partial match).

This is not full regex; only . and * are supported.


2. Examples:


Example 1:

Input: s = "aa", p = "a"

Output: false

Explanation:

  • Pattern "a" matches only one 'a'.

  • String is "aa" (two 'a's).

  • Entire string is not matched → false.


Example 2:

Input: s = "aa", p = "a*"

Output: true

Explanation:

  • a* means zero or more 'a'.

  • It can match "", "a", "aa", "aaa", etc.

  • So "aa" matches "a*" → true.


Example 3:

Input: s = "ab", p = ".*"

Output: true

Explanation:

  • . matches any single character.

  • .* means zero or more of any character.

  • So ".*" can match any string, including "ab".


Constraints:

  • 1≤∣s∣≤20

  • 1≤∣p∣≤20

  • s contains only lowercase English letters.

  • p contains lowercase letters, . and *.

  • Every * in p has a valid preceding character.


3. Approach 1 – Recursive Backtracking (Conceptual):


This is a natural way to think about it, but can be exponential without memoization.


Key Observations:


  1. p[j] can be:

    • a normal character ('a'–'z')

    • a dot ('.')

    • or a '*', but '*' always applies to the preceding character (p[j-1]).

  2. Consider the state (i, j):

    • i → current index in s

    • j → current index in p

    • We want to know: does s[i:] match p[j:]?

  3. For each position j in pattern:

    • Case 1: j + 1 < len(p) and p[j+1] == '*'
      Then we have two choices for *:

      • Treat * as zero occurrences of p[j]:

        • Skip p[j] and '*': move to j + 2 (pattern jumps over x*).

      • Treat * as one or more occurrences, but only if s[i] matches p[j]:

        • Move i → i + 1 (consume one character from s), stay at j (pattern still at x* to possibly consume more).

    • Case 2: No * after p[j]
      We just match one character:

      • Check if s[i] matches p[j] (same char or '.').

      • If yes → move to (i+1, j+1), else → fail.

Base Case:

  • When j == len(p):

    • Match is successful only if i == len(s) (both strings fully consumed).

This leads to a recursive solution with memoization (DP). But it’s often presented as DP directly.


4. Approach 2 – Dynamic Programming (2D DP, Recommended):


We define a DP table:

  • dp[i][j] = does s[i:] match p[j:]?

Or equivalently:

  • dp[i][j] = does the prefix s[0..i-1] match the prefix p[0..j-1]?

We’ll use the prefix version (more common):

  • i from 0 to len(s)

  • j from 0 to len(p)

  • dp[i][j] is true if s[0..i-1] matches p[0..j-1]

Then:

  • Answer is dp[len(s)][len(p)].


Initialization:


  • dp[0][0] = true → empty string matches empty pattern.

  • dp[0][j] (empty string vs pattern):

Pattern like "a*b*c*" can match empty string.

So for j >= 2:

  • If p[j-1] == '*', then:

    • dp[0][j] = dp[0][j-2]

(we ignore the preceding char + * pair).


Transition:


For i >= 1, j >= 1:

  1. If p[j-1] is NOT '*' (i.e., normal char or '.'):

    • We need one character match:

      • s[i-1] == p[j-1] or p[j-1] == '.'

      • and the prefixes up to i-1, j-1 must match:

if p[j-1] == s[i-1] or p[j-1] == '.':

    dp[i][j] = dp[i-1][j-1]

else:

    dp[i][j] = false

  1. If p[j-1] == '*':

    • Then it applies to p[j-2] (the preceding element). We have two subcases:

    • Let x = p[j-2]:

      • Zero occurrences of x:
        Ignore x* pair:

    dp[i][j] = dp[i][j-2]

  • One or more occurrences of x:
    Only if x matches s[i-1] (same char or '.'):

  • If s[i-1] matches x, then we can say:

    • We use one x to match s[i-1], and we keep x* to potentially match more characters.

    • So:

dp[i][j] = dp[i][j] OR dp[i-1][j]


Combining:



Pseudo-code (DP, Bottom-up):



Complexity:


Let:

  • m=∣s∣

  • n=∣p∣

Then:

  • Time: O(m⋅n)

  • Space: O(m⋅n)

Space can be reduced to O(n) with rolling arrays, but O(mn) is fine given constraints.


5. Java code:



6. C code:



7. C++ code:



8. Python code:



9. JavaScript code:





NEXT>>>

Comments

Popular posts from this blog

Leetcode 1: Two Sum

  Leetcode Link: https://leetcode.com/problems/two-sum/ 1. Problem Statement (Simple Explanation) You are given: An array of integers nums An integer target You need to  return the indices of two numbers in the array  such that: Conditions: Each input has  exactly one solution . You  cannot use the same element twice . You can return the answer in  any order . 2. Examples Example 1 Input:                                    nums = [2, 7, 11, 15], target = 9 Output:                                 [0, 1] Explanation:                     nums[0]+nums[1]=2+7=9              So we return [0, 1]. Example 2 Input:           ...

Leetcode 3: Longest Substring Without Repeating Characters

  Leetcode Link 1. Problem Statement (Simple Explanation) You are given a string s. You need to find the length of the longest substring of s that does not  contain any repeated characters. A substring is a contiguous sequence of characters in the string. You must return an integer length, not the substring itself. 2. Examples Example 1 Input: s = "abcabcbb" Output: 3 Explanation: Some substrings without repeating characters: "abc", "bca", "cab"The longest length is 3. Example 2 Input: s = "bbbbb" Output: 1 Explanation: All substrings that are valid are just "b". The longest length is 1. Example 3 Input:   s = "pwwkew" Output:   3 Explanation: Possible substrings without repetition: "pw", "wke", "kew". "wke" or "kew" have length 3. "pwke" is not valid because it’s not contiguous in the original string (p and k are separated by ww). Constraints...

Leetcode 2: Add Two Numbers

Leetcode Link: https://leetcode.com/problems/add-two-numbers/ 1. Problem Statement (Simple Explanation) You are given  two non-empty linked lists , l1 and l2, that represent two non-negative integers. Each node contains a  single digit . The digits are stored in  reverse order  (i.e., the 1’s digit is at the head). You need to  add the two numbers  and return the  sum as a linked list , also in reverse order. You can assume: There are  no leading zeros , except when the number itself is 0. 2. Examples Example 1 Input:                l1 = [2,4,3]                l2 = [5,6,4]           Interpreting linked lists as numbers (reverse order): l1 represents 342 l2 represents 465 Output:                [7,0,8] Explanation:        ...