<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>

class Solution:
    @cache
    def possiblyEquals(self, s1: str, s2: str) -> bool:
        # If the first character of s1 or s2 is '0', skip it and recurse because '0' can't be a valid length.
        if s1 and s1[0] == '0': return self.possiblyEquals(s1[1:],s2[:])
        if s2 and s2[0] == '0': return self.possiblyEquals(s1[:],s2[1:])
        
        # If both strings are empty, it means all characters have been matched correctly.
        if not s1 and not s2: return True
        # If one string is empty and the other is not, it means they can't possibly match.
        if not s1 or not s2: return False
        # If the first characters of both strings are not digits and they don't match, return False.
        if not s1[0].isdigit() and not s2[0].isdigit() and s1[0] != s2[0]: return False

        # At this point, either s1 starts with a number or s2 starts with a number, or both. Then just try all the possibilities for numbers.  

        res = False
        # If the first characters of s1 and s2 match, recurse with the rest of the strings.
        if s1[0] == s2[0]:
            res = self.possiblyEquals(s1[1:],s2[1:])
        
        # Extract prefix numbers from s1 and s2 to handle encoded lengths.
        nums1 = self.getPrefixNumbers(s1)  
        nums2 = self.getPrefixNumbers(s2)  
        
        # If s1 starts with a letter, try matching it with the numeric lengths in s2.
        if not nums1:
            for i in range(0,len(nums2)):
                num = nums2[i]
                res = res or self.possiblyEquals(s1[1:], str(num-1) + s2[i+1:])

        # If s2 starts with a letter, try matching it with the numeric lengths in s1.
        if not nums2:
            for i in range(0,len(nums1)):
                num = nums1[i]
                res = res or self.possiblyEquals(str(num-1) + s1[i+1:], s2[1:])
        
        # For numeric lengths in both s1 and s2, try all combinations of matching them.
        for i in range(0,len(nums1)):
            for j in range(0,len(nums2)):
                num1, num2 = nums1[i], nums2[j]
                # If num1 > num2, subtract num2 from num1 and recurse with the remaining parts.
                if num1 > num2:
                    res = res or self.possiblyEquals(str(num1-num2) + s1[i+1:], s2[j+1:])
                # If num1 < num2, subtract num1 from num2 and recurse with the remaining parts.
                if num1 < num2:
                    res = res or self.possiblyEquals(s1[i+1:], str(num2-num1) + s2[j+1:])
                # If num1 == num2, they cancel each other out, and we recurse with the remaining parts.
                if num1 == num2:
                    res = res or self.possiblyEquals(s1[i+1:], s2[j+1:])
        
        return res
    
    def getPrefixNumbers(self, s: str):
        # Extract up to two digits from the start of the string as possible lengths.
        res = []
        if s and s[0].isdigit():
            res = [int(s[0])]
            # If the second character is also a digit, add the two-digit number as a possibility.
            if len(s) > 1 and s[1].isdigit():
                res.append(int(s[0:2]))
        return res
1657