<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 typing import List, Union, Tuple


class Solution:
    def possiblyEquals(self, s1: str, s2: str) -> bool:
        def split_digits_and_letters(s: str) -> Tuple[str, ...]:
            ret = []
            i = 0
            j = i + 1
            while i < len(s):
                j = i
                while j < len(s) and s[j].isdigit() == s[i].isdigit():
                    j += 1
                ret.append(s[i:j])
                i = j
            return tuple(ret)
        mem = {((), ()): True}
        def equal(t1: Tuple[Union[str, int], ...], t2: Tuple[Union[str, int], ...]) -> bool:
            nonlocal mem
            cached = mem.get((t1, t2))
            if cached is not None:
                return cached
            if t1 == () or t2 == ():
                return False
            def gen_possible_digits_combinations(s: str) -> List[int]:
                assert s.isdigit() and 1 <= len(s) <= 3
                if len(s) == 1:
                    return [int(s)]
                elif len(s) == 2:
                    return [int(s[0]) + int(s[1]), int(s)]
                elif len(s) == 3:
                    return [int(s[0]) + int(s[1]) + int(s[2]), int(s[0]) + int(s[1:]), int(s[:2]) + int(s[2]), int(s)]
            if type(t1[0]) == str and t1[0].isdigit():
                possible_elems1 = gen_possible_digits_combinations(t1[0])
            else:
                possible_elems1 = [t1[0]]
            if type(t2[0]) == str and t2[0].isdigit():
                possible_elems2 = gen_possible_digits_combinations(t2[0])
            else:
                possible_elems2 = [t2[0]]
            ret = None
            for elem1 in possible_elems1:
                for elem2 in possible_elems2:
                    if type(elem1) == type(elem2) == int:
                        min_val = min(elem1, elem2)
                        new_elem1 = elem1 - min_val
                        if new_elem1 > 0:
                            new_t1 = (new_elem1,) + t1[1:]
                        else:
                            new_t1 = t1[1:]
                        new_elem2 = elem2 - min_val
                        if new_elem2 > 0:
                            new_t2 = (new_elem2,) + t2[1:]
                        else:
                            new_t2 = t2[1:]
                        if equal(new_t1, new_t2):
                            ret = True
                    elif type(elem1) == type(elem2) == str:
                        min_len = min(len(elem1), len(elem2))
                        if elem1[:min_len] != elem2[:min_len]:
                            ret = False
                        else:
                            new_elem1 = elem1[min_len:]
                            new_elem2 = elem2[min_len:]
                            if new_elem1 != '':
                                new_t1 = (new_elem1,) + t1[1:]
                            else:
                                new_t1 = t1[1:]
                            if new_elem2 != '':
                                new_t2 = (new_elem2,) + t2[1:]
                            else:
                                new_t2 = t2[1:]
                            if equal(new_t1, new_t2):
                                ret = True
                    elif type(elem1) == int and type(elem2) == str:
                        min_len = min(elem1, len(elem2))
                        new_elem1 = elem1 - min_len
                        new_elem2 = elem2[min_len:]
                        if new_elem1 > 0:
                            new_t1 = (new_elem1,) + t1[1:]
                        else:
                            new_t1 = t1[1:]
                        if new_elem2 != '':
                            new_t2 = (new_elem2,) + t2[1:]
                        else:
                            new_t2 = t2[1:]
                        if equal(new_t1, new_t2):
                            ret = True
                    elif type(elem1) == str and type(elem2) == int:
                        min_len = min(len(elem1), elem2)
                        new_elem1 = elem1[min_len:]
                        new_elem2 = elem2 - min_len
                        if new_elem1 != '':
                            new_t1 = (new_elem1,) + t1[1:]
                        else:
                            new_t1 = t1[1:]
                        if new_elem2 > 0:
                            new_t2 = (new_elem2,) + t2[1:]
                        else:
                            new_t2 = t2[1:]
                        if equal(new_t1, new_t2):
                            ret = True
                if ret is not None:
                    break
            if ret is None:
                ret = False
            mem[(t1, t2)] = ret
            return ret
        t1, t2 = split_digits_and_letters(s1), split_digits_and_letters(s2)
        return equal(t1, t2)
1591