Tuesday, June 27, 2017

131. Palindrome Partitioning

四刷 07/2022
Version #1 Bottom-up DFS with memorization

Time - There are O(2^N) possible partitions and for each partition we use O(N) to get the substring
So total time complexity is O(N*2^N)
Space O(N^2)
Runtime: 21 ms, faster than 46.18% of Java online submissions for Palindrome Partitioning.
Memory Usage: 183.8 MB, less than 65.31% of Java online submissions for Palindrome Partitioning.

class Solution {
    public List<List<String>> partition(String s) {
        int len = s.length();
        boolean[][] isPalin = new boolean[len][len];
        for (int j = 0; j < len; j++) {
            for (int i = j; i >= 0; i--) {
                if (i == j) {
                    isPalin[i][j] = true;
                } else if (i + 1 == j) {
                    isPalin[i][j] = s.charAt(i) == s.charAt(j);
                } else {
                    isPalin[i][j] = s.charAt(i) == s.charAt(j) && isPalin[i + 1][j - 1];
                }
            }
        }
        return helper(s, 0, new HashMap<>(), isPalin);
    }
    // result of palindrome partitioning of substring index
    private List<List<String>> helper(String s, int startIndex, Map<Integer, List<List<String>>> mem, boolean[][] isPalin) {
        if (mem.containsKey(startIndex)) {
            return mem.get(startIndex);
        }
        List<List<String>> result = new ArrayList<>();
        if (startIndex == s.length()) {
            result.add(new ArrayList<>(Arrays.asList(new String[]{})));
            return result;
        }
        
        for (int endIndex = startIndex; endIndex < s.length(); endIndex++) {
            if (!isPalin[startIndex][endIndex]) {
                continue;
            }
            String curr = s.substring(startIndex, endIndex + 1);
            List<List<String>> nextResult = helper(s, endIndex + 1, mem, isPalin);
            for (List<String> next : nextResult) {
                List<String> subResult = new ArrayList<>();
                subResult.add(curr);
                subResult.addAll(next);
                result.add(subResult);
            }
        }
        mem.put(startIndex, result);
        return result;
    }
}



三刷 06/2022
Version #2 Top-bottom DFS with memorization 
class Solution {
    public List<List<String>> partition(String s) {
        List<List<String>> result = new ArrayList<>();
        dfs(s, 0, new ArrayList<>(), result, new Boolean[s.length() + 1][s.length() + 1]);
        return result;
    }
    
    private void dfs(String s, int index, List<String> path, List<List<String>> result, Boolean[][] dp) {
        // System.out.printf("index %d\n", index);
        if (index == s.length()) {
            result.add(new ArrayList<>(path));
            return;
        }
        for (int i = s.length() - 1; i >= index; i--) {
            String substr = s.substring(index, i + 1);
            if (!isPalin(s, index, i, dp)) {
                continue;
            }
            path.add(substr);
            // System.out.printf("substr %s added\n", substr);
            dfs(s, i + 1, path, result, dp);
            path.remove(path.size() - 1);
        }
    }
    // Returns if substring [start, end] is palindrome
    private boolean isPalin(String s, int start, int end, Boolean[][] dp) {
        if (start >= end) {
            return true;
        }
        if (dp[start][end] != null) {
            return dp[start][end];
        }
        dp[start][end] = s.charAt(start) == s.charAt(end) && isPalin(s, start + 1, end - 1, dp);
        return dp[start][end];
    }
}

Version #1 Bottom-up DFS with memorization - SLOW

class Solution {
    public List<List<String>> partition(String s) {
        // TODO
        return dfs(s, 0, new HashMap<>());
    }
    
    // Returns all the palindrome partition of substring [index, s.length())
    private List<List<String>> dfs(String s, int index, Map<Integer, List<List<String>>> mem) {
        // System.out.printf("index=%d, memsize=%d\n", index, mem.size());
        if (index >= s.length()) {
            return new ArrayList<>();
        }
        if (mem.containsKey(index)) {
            return mem.get(index);
        }
        List<List<String>> result = new ArrayList<>();
        for (int end = index + 1; end <= s.length(); end++) {
            String word = s.substring(index, end);
            // System.out.printf("word:%s\n", word);
            if (isPalindrome(word)) {
                if (end == s.length()) {
                    result.add(Arrays.asList(new String[]{word}));
                }
                List<List<String>> sub = dfs(s, end, mem);
                for (List<String> su : sub) {
                    List<String> temp = new LinkedList<>(su);
                    temp.add(0, word);
                    result.add(temp);
                }
            }
        }
        mem.put(index, result);
        return result;
    }
    private boolean isPalindrome(String s) {
        int start = 0, end = s.length() - 1;
        while (start < end) {
            if (s.charAt(start) != s.charAt(end)) {
                return false;
            }
            start++;
            end--;
        }
        return true;
    }
}

二刷
整体和之前写的一样,pruning的位置不一样了
我认为时间复杂度和空间复杂度都是 O(n^2)
时间复杂度是把 mem[n][n] fill out
96.11 %

class Solution {
    public List<List<String>> partition(String s) {
        List<List<String>> result = new ArrayList<>();
        if (s == null) {
            return result;
        }
        Boolean[][] mem = new Boolean[s.length()][s.length()];
        helper(s, 0, mem, new ArrayList<>(), result);
        return result;
    }
    private void helper(String s, int startIndex, Boolean[][] mem, List<String> path, List<List<String>> result) {
        // Substring before startIndex has been partitioned into palindrome strings
        // If we can reach the end of s, means the whole string is partitioned
        if (startIndex == s.length()) {
            result.add(new ArrayList<>(path));
            return;
        }
        // [startIndex, endIndex]
        for (int endIndex = startIndex; endIndex < s.length(); endIndex++) {
            boolean isPalin = false;
            if (mem[startIndex][endIndex] != null) {
                isPalin = mem[startIndex][endIndex];
            } else {
                isPalin = isPalindrome(s, startIndex, endIndex);
                mem[startIndex][endIndex] = isPalin;
            }
            if (isPalin) {
                path.add(s.substring(startIndex, endIndex + 1));
                helper(s, endIndex + 1, mem, path, result);
                path.remove(path.size() - 1);
            }
        }
    }
   
    private boolean isPalindrome(String s, int startIndex, int endIndex) {
        while (startIndex < endIndex) {
            if (s.charAt(startIndex) != s.charAt(endIndex)) {
                return false;
            }
            startIndex++;
            endIndex--;
        }
        return true;
    }
}


一刷
因为这里一个single character也可以作为palindrome, 所以没有办法用boolean array 做pruning, 因为所有的path都会是true
我用一个Boolean[][]记录check过的palindrome, 如果下一次遇到同样的startIndex和endIndex,就取之前的结果

时间复杂度?
O(n^2)??
public class Solution {
    private Boolean[][] palindromeDict;
    public List<List<String>> partition(String s) {
        List<List<String>> result = new ArrayList<>();
        if (s == null) return result;
        //Boolean[i][j] string[i, j] isPalindrome or not
        palindromeDict = new Boolean[s.length()][s.length()];
        partitionDFSHelper(0, s, new ArrayList<String>(), result);
        return result;
    }
    private void partitionDFSHelper(int startIndex, String s, List<String> path, List<List<String>> result) {
        if (startIndex == s.length()) {
            result.add(new ArrayList<String>(path));
        }
        for (int i = startIndex; i < s.length(); i++) {
            if (isPalindrome(s, startIndex, i)) {
                path.add(s.substring(startIndex, i + 1));
                partitionDFSHelper(i + 1, s, path, result);
                path.remove(path.size() - 1);
            }
        }
    }
    private boolean isPalindrome(String s, int start, int end) {
        if (palindromeDict[start][end] != null) return palindromeDict[start][end];
     
        while (start < end) {
            if (s.charAt(start) != s.charAt(end)) {
                palindromeDict[start][end] = false;
                return false;
            }
            start++;
            end--;
        }
        palindromeDict[start][end] = true;
        return true;
    }
}

No comments:

Post a Comment