首页 > 代码库 > leetcode-Palindrome Partitioning II

leetcode-Palindrome Partitioning II

Palindrome Partitioning II

 Total Accepted: 11791 Total Submissions: 66110

Given a string s, partition s such that every substring of the partition is a palindrome.

Return the minimum cuts needed for a palindrome partitioning of s.

For example, given s = "aab",
Return 1 since the palindrome partitioning ["aa","b"] could be produced using 1 cut.

Have you been asked this question in an interview? 


这道题是Palindrome Partitioning的变形,要求求出最小的切割使得所有切割后的子串都是回文串。


可以在Palindrome Partitioning的基础上修改,在找到可行解时,更新最优解。

class Solution {
public:
    int minCut(string s) {
        ans = INT_MAX;
        findMinCut(s, 0, 0);
        return ans;
    }
private:
    int ans;
    
    void findMinCut(const string &s, int k, int currCut) {
        string substr;
        for (int i=k; i<s.size(); ++i) {
            if (isPalindrome(s, k, i)) {
                if (i+1 == s.size()) {
                    if (currCut+1 < ans) {
                        ans = currCut+1;
                    }
                } else {
                    findMinCut(s, i+1, currCut+1);
                }
            }
        }
    }
    
    bool isPalindrome(const string &s, int b, int e) {
        int i=b, j=e;
        while (i<j) {
            if (s[i] != s[j])
                return false;
            ++i;
            --j;
        }
        return true;
    }
};

如果增加记忆化搜索+剪枝,还是会TLE。超时的case是一大串"aaaa...aa"的情况。

class Solution {
public:
    int minCut(string s) {
        minCutNum = INT_MAX;
        vector<string> paths;
        find(s, 0, paths);
        return minCutNum;
    }
private:
    int minCutNum;
    map<int, int> minCutsMap;
    void find(const string& s, int ind, vector<string>& paths)
    {
        for (int i = ind; i < s.size(); ++i) {
            if (isPalindrome(s, ind, i)) {
                if (paths.size() > minCutNum)
                    continue;
                if (minCutsMap.find(i+1) != minCutsMap.end()) {
                    if (paths.size() + minCutsMap.size() - 1 < minCutNum) {
                        minCutNum = paths.size() + minCutsMap.size() - 1;
                    }
                    continue;
                }
                paths.push_back(s.substr(ind, i - ind + 1));
                if (i + 1 == s.size()) {
                    if (minCutNum > paths.size() - 1) {
                        minCutNum = paths.size() - 1;
                    }
                    paths.pop_back();
                    continue;
                }
                int num = paths.size();
                minCutsMap[i + 1] = INT_MAX;
                find(s, i + 1, paths);
                if (minCutsMap[i + 1] > paths.size() - num) {
                    minCutsMap[i + 1] = paths.size() - num;
                }
                if (minCutNum == 0)
                    return;
                paths.pop_back();
            }
        }
    }
    bool isPalindrome(const string& str, int begin, int end)
    {
        while (begin < end) {
            if (str[begin] != str[end])
                return false;
            ++begin; --end;
        }
        return true;
    }
};

最后网上参考了别人的DP,自己写了下,终于看到Accepted。初始化isPalindrome的方法也是用了DP的方法,跟计算矩阵连乘最小操作数类似。假设字符串为str,长度为n,计算最小切割数的状态转移方程为:dp[i] = min{dp[j]+1,dp[i]}(j=i+1...n-1), 其中dp[i]为str[i]...str[n-1]的最小切割数。在做DP题时,要考虑清楚怎么存状态,以及状态是如何转移的。DP的优点就是能够根据以前的选择来做出当下最优的选择,像贪心算法就不能,只有以前局部最优的状态。

class Solution {
public:
    int minCut(string s) {
        int len = s.size();
        bool **isPalindrome = new bool*[len];
        for (int i = 0; i < len; ++i) {
            isPalindrome[i] = new bool[len];
        }
        initIsPalindrome(isPalindrome, s);
        
        int *dp = new int[len]; // dp[i] stores minimum cuts for s[i]...s[len-1]
        for (int i = len - 1; i >= 0; --i) {
            if (isPalindrome[i][len - 1]) {
                dp[i] = 0;
                continue;
            }
            dp[i] = INT_MAX;
            for (int j = i + 1; j < len; ++j) {
                if (isPalindrome[i][j - 1]) {
                    if (dp[j] + 1 < dp[i]) {
                        dp[i] = dp[j] + 1;
                    }
                }
            }
        }
        int ret = dp[0];
        for (int i = 0; i < len; ++i)
            delete []isPalindrome[i];
        delete []isPalindrome;
        delete dp;
        return ret;
    }
private:
    void initIsPalindrome(bool ** isPalindrome, const string& s) {
        int len = s.length();
        for (int L = 1; L <= len; ++L) { // L is the length of substring, from 1 to len
            for (int i = 0; i < len - L + 1; ++i) { // i is the starting index
                int j = i + L - 1; // j is the ending index
                if (L == 1) {
                    isPalindrome[i][j] = true;
                } else if (L == 2) {
                    isPalindrome[i][j] = s[i] == s[j];
                } else {
                    isPalindrome[i][j] = (s[i] == s[j]) && isPalindrome[i + 1][j - 1];
                }
            }
        }
    }
};