Problems
0139 - Word Break
Easy
/**
 * Brute Force - DFS
 * Hash Set - Distinct Keys
 * Time O(2^N) | Space O(N)
 * https://leetcode.com/problems/word-break/
 * @param {string} s
 * @param {string[]} wordDict
 * @return {boolean}
 */
 var wordBreak = (s, wordDict) => {
    const wordSet = new Set(wordDict);/* Time O(N)   | Space O(N) */

    return canBreak(s, wordSet);      /* Time O(2^N) | Space O(N) */
};

var canBreak = (s, wordSet, start = 0) => {
    const isBaseCase = (start === s.length);
    if (isBaseCase) return true;

    return dfs(s, wordSet, start);/* Time O(2^N) | Space O(N) */
}

var dfs = (s, wordSet, start) => {
    for (let end = (start + 1); end <= s.length; end++) {/* Time O(N) */
        const word = s.slice(start, end);                    /* Time O(N)   | Space O(N) */

        const _canBreak = wordSet.has(word)
            && canBreak(s, wordSet, end);                    /* Time O(2^N) | Space O(N) */
        if (_canBreak) return true;
    }

    return false;
}

/**
 * DP - Top Down
 * Array - Memoization
 * Hash Set - Distinct Keys
 * Time O(N^3) | Space O(N)
 * https://leetcode.com/problems/word-break/
 * @param {string} s
 * @param {string[]} wordDict
 * @return {boolean}
 */
var wordBreak = (s, wordDict) => {
    const wordSet = new Set(wordDict);           /* Time O(N)         | Space O(N) */
    const memo = new Array(s.length).fill(null); /*                   | Space O(N) */
    const start = 0;

    return canBreak(s, wordSet, start, memo);    /* Time O(N * N * N) | Space O(N) */
}

var canBreak = (s, wordSet, start, memo) => {
    const isBaseCase1 = (s.length === start);
    if (isBaseCase1) return true;

    const hasSeen = (memo[start] !== null);
    if (hasSeen) return memo[start];

    return dfs(s, wordSet, start, memo);/* Time O(N * N * N) | Space O(N) */
}

var dfs = (s, wordSet, start, memo) => {
    for (let end = (start + 1); (end <= s.length); end++) {/* Time O(N) */
        const word = s.slice(start, end);                      /* Time O(N) | Space O(N) */

        const _canBreak = wordSet.has(word)
            && canBreak(s, wordSet, end, memo);                /* Time O(N * N) */
        if (_canBreak) {
            memo[start] = true;
            return true;
        }
    }

    memo[start] = false;
    return false;
}

/**
 * DP - Bottom Up
 * Array - Tabulation
 * Hash Set - Distinct Keys
 * Time O(N^3) | Space O(N)
 * https://leetcode.com/problems/word-break/
 * @param {string} s
 * @param {string[]} wordDict
 * @return {boolean}
 */
 var wordBreak = (s, wordDict) => {
    const wordSet = new Set(wordDict);/* Time O(N)         | Space O(N) */
    const tabu = initTabu(s);         /*                   | Space O(N) */

    canBreak(s, wordSet, tabu);       /* Time O(N * N * N) | Space O(N) */

    return tabu[s.length];
}

const initTabu = (s) => {
    const tabu = new Array((s.length + 1)).fill(false);/* Space O(N) */

    tabu[0] = true;

    return tabu;
}

var canBreak = (s, wordSet, tabu) => {
    for (let end = 1; (end <= s.length); end++) {/* Time O(N) */
        checkWord(s, wordSet, end, tabu);            /* Time O(N * N) | Space O(N) */
    }
}

var checkWord = (s, wordSet, end, tabu) => {
    for (let start = 0; (start < end); start++) {/* Time O(N) */
        const word = s.slice(start, end);            /* Time O(N) | Space O(N) */

        const canBreak = tabu[start] && wordSet.has(word);
        if (!canBreak) continue;

        tabu[end] = true;

        return;
    }
}

/**
 * Tree Traversal - BFS
 * Queue - Level Order Space O(WIDTH)
 * Hash Set - Distinct Keys
 * Array - Seen
 * Time O(N^3) | Space O(N)
 * https://leetcode.com/problems/word-break/
 * @param {string} s
 * @param {string[]} wordDict
 * @return {boolean}
 */
var wordBreak = function(s, wordDict) {
    const wordSet = new Set(wordDict);           /* Time O(N)         | Space O(N) */
    const queue = new Queue([ 0 ]);              /*                   | Space O(N) */
    const seen = new Array(s.length).fill(false);/*                   | Space O(N) */

    return bfs(queue, s, wordSet, seen);         /* Time O(N * N * N) | Space O(N + WIDTH) */
}

const bfs = (queue, s, wordSet, seen) => {
    while (!queue.isEmpty()) {
        for (let level = (queue.size() - 1); (0 <= level); level--) {/* Time O(N) */
            if (canWordBreak(queue, s, wordSet, seen)) return true;      /* Time O(N * N) | Space O(N + WIDTH) */
        }
    }

    return false;
}

var canWordBreak = (queue, s, wordSet, seen) => {
    const start = queue.dequeue();

    const hasSeen = seen[start];
    if (hasSeen) return false;

    if (canBreak(queue, s, start, wordSet)) return true;/* Time O(N * N) | Space O(N + WIDTH) */

    seen[start] = true;                                 /*               | Space O(N) */
    return false;
}

var canBreak = (queue, s, start, wordSet) => {
    for (let end = start + 1; end <= s.length; end++) {/* Time O(N) */
        const word = s.slice(start, end);                  /* Time O(N) | Space O(N) */

        if (!wordSet.has(word)) continue;

        queue.enqueue(end);                                /*           | Space O(WIDTH) */

        const _canBreak = end === s.length;
        if (_canBreak) return true;
    }

    return false
}