<p>An original string, consisting of lowercase English letters, can be encoded by the following steps:</p>

<ul>
	<li>Arbitrarily <strong>split</strong> it into a <strong>sequence</strong> of some number of <strong>non-empty</strong> substrings.</li>
	<li>Arbitrarily choose some elements (possibly none) of the sequence, and <strong>replace</strong> each with <strong>its length</strong> (as a numeric string).</li>
	<li><strong>Concatenate</strong> the sequence as the encoded string.</li>
</ul>

<p>For example, <strong>one way</strong> to encode an original string <code>&quot;abcdefghijklmnop&quot;</code> might be:</p>

<ul>
	<li>Split it as a sequence: <code>[&quot;ab&quot;, &quot;cdefghijklmn&quot;, &quot;o&quot;, &quot;p&quot;]</code>.</li>
	<li>Choose the second and third elements to be replaced by their lengths, respectively. The sequence becomes <code>[&quot;ab&quot;, &quot;12&quot;, &quot;1&quot;, &quot;p&quot;]</code>.</li>
	<li>Concatenate the elements of the sequence to get the encoded string: <code>&quot;ab121p&quot;</code>.</li>
</ul>

<p>Given two encoded strings <code>s1</code> and <code>s2</code>, consisting of lowercase English letters and digits <code>1-9</code> (inclusive), return <code>true</code><em> if there exists an original string that could be encoded as <strong>both</strong> </em><code>s1</code><em> and </em><code>s2</code><em>. Otherwise, return </em><code>false</code>.</p>

<p><strong>Note</strong>: The test cases are generated such that the number of consecutive digits in <code>s1</code> and <code>s2</code> does not exceed <code>3</code>.</p>

<p>&nbsp;</p>
<p><strong class="example">Example 1:</strong></p>

<pre>
<strong>Input:</strong> s1 = &quot;internationalization&quot;, s2 = &quot;i18n&quot;
<strong>Output:</strong> true
<strong>Explanation:</strong> It is possible that &quot;internationalization&quot; was the original string.
- &quot;internationalization&quot; 
  -&gt; Split:       [&quot;internationalization&quot;]
  -&gt; Do not replace any element
  -&gt; Concatenate:  &quot;internationalization&quot;, which is s1.
- &quot;internationalization&quot;
  -&gt; Split:       [&quot;i&quot;, &quot;nternationalizatio&quot;, &quot;n&quot;]
  -&gt; Replace:     [&quot;i&quot;, &quot;18&quot;,                 &quot;n&quot;]
  -&gt; Concatenate:  &quot;i18n&quot;, which is s2
</pre>

<p><strong class="example">Example 2:</strong></p>

<pre>
<strong>Input:</strong> s1 = &quot;l123e&quot;, s2 = &quot;44&quot;
<strong>Output:</strong> true
<strong>Explanation:</strong> It is possible that &quot;leetcode&quot; was the original string.
- &quot;leetcode&quot; 
  -&gt; Split:      [&quot;l&quot;, &quot;e&quot;, &quot;et&quot;, &quot;cod&quot;, &quot;e&quot;]
  -&gt; Replace:    [&quot;l&quot;, &quot;1&quot;, &quot;2&quot;,  &quot;3&quot;,   &quot;e&quot;]
  -&gt; Concatenate: &quot;l123e&quot;, which is s1.
- &quot;leetcode&quot; 
  -&gt; Split:      [&quot;leet&quot;, &quot;code&quot;]
  -&gt; Replace:    [&quot;4&quot;,    &quot;4&quot;]
  -&gt; Concatenate: &quot;44&quot;, which is s2.
</pre>

<p><strong class="example">Example 3:</strong></p>

<pre>
<strong>Input:</strong> s1 = &quot;a5b&quot;, s2 = &quot;c5b&quot;
<strong>Output:</strong> false
<strong>Explanation:</strong> It is impossible.
- The original string encoded as s1 must start with the letter &#39;a&#39;.
- The original string encoded as s2 must start with the letter &#39;c&#39;.
</pre>

<p>&nbsp;</p>
<p><strong>Constraints:</strong></p>

<ul>
	<li><code>1 &lt;= s1.length, s2.length &lt;= 40</code></li>
	<li><code>s1</code> and <code>s2</code> consist of digits <code>1-9</code> (inclusive), and lowercase English letters only.</li>
	<li>The number of consecutive digits in <code>s1</code> and <code>s2</code> does not exceed <code>3</code>.</li>
</ul>

from functools import lru_cache

class Solution:
    def possiblyEquals(self, s1: str, s2: str) -> bool:
        # helper to find the end index of a numeric substring
        def getEndIndex(s: str, start: int):
            index = start
            while index < len(s) and s[index].isdigit():
                index += 1
            return index

        # helper to return all possible lengths represented by a numeric string
        @lru_cache(None)
        def getPossibleLengths(s: str):
            lengths = {int(s)}
            for i in range(1, len(s)):
                left_part = s[:i]
                right_part = s[i:]

                # recursively find lengths of left and right side
                left_lengths = getPossibleLengths(left_part)
                right_lengths = getPossibleLengths(right_part)

                for left in left_lengths:
                    for right in right_lengths:
                        lengths.add(left + right)

            return lengths

        # main dp function to check if s1[i:] and s2[j:] can match with a given lead difference
        @lru_cache(None)
        def dp(i, j, diff):
            # reached the end
            if i == len(s1) and j == len(s2):
                return diff == 0

            # s1 is at a digit
            if i < len(s1) and s1[i].isdigit():
                index = getEndIndex(s1, i)
                substr = s1[i:index]
                for length in getPossibleLengths(substr):
                    if dp(index, j, diff - length):
                        return True

            # s2 is at a digit
            elif j < len(s2) and s2[j].isdigit():
                index = getEndIndex(s2, j)
                substr = s2[j:index]
                for length in getPossibleLengths(substr):
                    if dp(i, index, diff + length):
                        return True

            # no more digits and diff is zero, compare letters
            elif diff == 0:
                if i < len(s1) and j < len(s2) and s1[i] == s2[j]:
                    return dp(i+1, j+1, 0)
                return False

            # diff is different

            # s2 leads s1
            elif diff > 0:
                if i < len(s1):
                    return dp(i+1, j, diff-1)
                return False
            
            elif diff < 0:
                if j < len(s2):
                    return dp(i, j+1, diff+1)
                return False
            

            return False


        return dp(0, 0, 0)

# from functools import lru_cache

# class Solution:
#     def possiblyEquals(self, s1: str, s2: str) -> bool:

#         # Helper to find the end index of a numeric substring
#         def getValidPrefixLength(s, start):
#             end = start
#             while end < len(s) and s[end].isdigit():
#                 end += 1
#             return end
        
#         # Helper to return all possible lengths represented by a numeric string
#         @lru_cache(None)
#         def possibleLengths(s):
#             lengths = {int(s)}
#             for i in range(1, len(s)):
#                 lengths |= {x + y for x in possibleLengths(s[:i]) for y in possibleLengths(s[i:])}
#             return lengths
        
#         # Main dp function to check if s1[i:] and s2[j:] can match with a given lead difference
#         @lru_cache(None)
#         def dp(i, j, diff):
#             if i == len(s1) and j == len(s2):
#                 return diff == 0
            
#             # If s1 has digits, calculate possible lengths and reduce diff accordingly
#             if i < len(s1) and s1[i].isdigit():
#                 i2 = getValidPrefixLength(s1, i)
#                 for L in possibleLengths(s1[i:i2]):
#                     if dp(i2, j, diff - L):
#                         return True

#             # If s2 has digits, calculate possible lengths and increase diff accordingly
#             if j < len(s2) and s2[j].isdigit():
#                 j2 = getValidPrefixLength(s2, j)
#                 for L in possibleLengths(s2[j:j2]):
#                     if dp(i, j2, diff + L):
#                         return True

#             # If no digits and diff is zero, try matching characters
#             if diff == 0:
#                 if i < len(s1) and j < len(s2) and s1[i] == s2[j]:
#                     return dp(i + 1, j + 1, 0)
#                 return False
            
#             # Handle cases where diff is positive (s2 has lead) or negative (s1 has lead)
#             if diff > 0 and i < len(s1):
#                 return dp(i + 1, j, diff - 1)
#             if diff < 0 and j < len(s2):
#                 return dp(i, j + 1, diff + 1)
            
#             return False

#         return dp(0, 0, 0)

57311