Scramble String

hard string dp memoization

Problem

String s2 is a scramble of s1 if for some split k, either (left1 scrambles left2 AND right1 scrambles right2), or (left1 scrambles right2 AND right1 scrambles left2). Return whether s2 is a scramble of s1.

Inputs1 = "great", s2 = "rgeat"
Outputtrue
Split "great" at 1: "g" + "reat"; scramble "reat" to "reat" then swap to get "rgeat".

from functools import lru_cache

def is_scramble(s1, s2):
    @lru_cache(maxsize=None)
    def go(a, b):
        if a == b:
            return True
        if sorted(a) != sorted(b):
            return False
        n = len(a)
        for k in range(1, n):
            if go(a[:k], b[:k]) and go(a[k:], b[k:]):
                return True
            if go(a[:k], b[n - k:]) and go(a[k:], b[:n - k]):
                return True
        return False
    return go(s1, s2)
function isScramble(s1, s2) {
  const memo = new Map();
  function go(a, b) {
    const key = a + "|" + b;
    if (memo.has(key)) return memo.get(key);
    if (a === b) { memo.set(key, true); return true; }
    if ([...a].sort().join("") !== [...b].sort().join("")) { memo.set(key, false); return false; }
    const n = a.length;
    for (let k = 1; k < n; k++) {
      if (go(a.slice(0, k), b.slice(0, k)) && go(a.slice(k), b.slice(k))) { memo.set(key, true); return true; }
      if (go(a.slice(0, k), b.slice(n - k)) && go(a.slice(k), b.slice(0, n - k))) { memo.set(key, true); return true; }
    }
    memo.set(key, false);
    return false;
  }
  return go(s1, s2);
}
class Solution {
    Map<String, Boolean> memo = new HashMap<>();
    public boolean isScramble(String s1, String s2) {
        String key = s1 + "|" + s2;
        if (memo.containsKey(key)) return memo.get(key);
        if (s1.equals(s2)) { memo.put(key, true); return true; }
        char[] a = s1.toCharArray();
        char[] b = s2.toCharArray();
        Arrays.sort(a); Arrays.sort(b);
        if (!Arrays.equals(a, b)) { memo.put(key, false); return false; }
        int n = s1.length();
        for (int k = 1; k < n; k++) {
            if (isScramble(s1.substring(0, k), s2.substring(0, k))
                && isScramble(s1.substring(k), s2.substring(k))) { memo.put(key, true); return true; }
            if (isScramble(s1.substring(0, k), s2.substring(n - k))
                && isScramble(s1.substring(k), s2.substring(0, n - k))) { memo.put(key, true); return true; }
        }
        memo.put(key, false);
        return false;
    }
}
unordered_map<string, bool> memo;
bool isScramble(string s1, string s2) {
    string key = s1 + "|" + s2;
    auto it = memo.find(key);
    if (it != memo.end()) return it->second;
    if (s1 == s2) return memo[key] = true;
    string a = s1, b = s2;
    sort(a.begin(), a.end()); sort(b.begin(), b.end());
    if (a != b) return memo[key] = false;
    int n = s1.size();
    for (int k = 1; k < n; k++) {
        if (isScramble(s1.substr(0, k), s2.substr(0, k))
            && isScramble(s1.substr(k), s2.substr(k))) return memo[key] = true;
        if (isScramble(s1.substr(0, k), s2.substr(n - k))
            && isScramble(s1.substr(k), s2.substr(0, n - k))) return memo[key] = true;
    }
    return memo[key] = false;
}
Time: O(n^4) Space: O(n^3)