Palindrome partition I II

这道题目引发出自己一直以来的一个思考,什么时候是二维DP,什么时候一维DP。

清晰记得如果一维DP足以表示状态,优先一维DP,这是算导的说法。

例如word break 一开始自己写二维DP,受了入门矩阵连的清晰,只要是区间DP,都是二维的,但是其实一维足以。
石子合并当时和谢老师讨论的时候,他说一维DP,我说会漏状态,所以还是二维DP比较合适。

现在回到这个palindrom partition问题来了,I 如果用一维DP完全可以,但是里面需要自己学一个isPalindrome函数,复杂度小算了一下,

sigma (i=1->n) sigma(j=0->i) (i-j) =1/2(1/6 n(n+1)*(2n+1) + n(n+1)/2) =O(n^3)

是三方的,虽然A了,但是因为这题需要返回结果,后面dfs,最坏是2^n个解,所以包容了这个复杂度,但是如果只是判定问题,例如
判定一个串是否可以partition palindrome的话,增加空间可以降低时间到平方的。例如这里的dp[i][j], 表示s(i…j) 是否可以划分回文串集合。
一维的dp[i]:似乎i作为长度更好,因为dp[0]是空的,但是注意 dp[i]往往和s[i-1] 关联如果作为索引考虑的东西会更多,例如单独处理dp[0], 看到小胖几乎都是这样的。

如果移步到II,会发现我之前的dp[] 超时了,在一个1500长度的串,三方不能忍了,因此开空间降复杂度,一般来说时间更重要,因此以后尽量开大状态。

G[] 要注意初始化为i-1, 然后特判j=0的情况
G[i]表示长度为i前缀的mincut次数,后面只需要看是否是回文就可以了,但是j=0 前面空串,后面整个串的情况要特殊处理一下。
一下是II的代码,是n^2的,因为没有判回文的函数,裸地两重循环

class Solution {
public:
    bool dp[1500][1500];
    int G[10000];
    bool isPa(string s)
    {
        int n=s.size();
        for(int i =0, j=n-1;i<j;i++, j--)
            if(s[i]!=s[j]) return 0;
        return 1;
    }
    int minCut(string s)
    {
        int n=s.size();
        for(int i=0;i<n;i++) dp[i][i]=1;
        for(int i=0;i<n-1;i++) dp[i][i+1]=s[i]==s[i+1];
        for(int len=3;len<=n;len++)
        {
            for(int i=0;i<n;i++)
            {
                int j=i+len-1;
                if(j>=n) continue;
                dp[i][j]= dp[i+1][j-1] && s[i]==s[j];
            }
        }
        G[0]=0;
        for(int i=1;i<=n;i++)
        {
            G[i]=i-1;
            for(int j=0;j<i;j++)
            {
                if(dp[j][i-1])
                    G[i]=min(G[i],j>0 ? G[j]+1 : 0);
            }
        }
        return G[n];
    }
};

以下是I的代码,三方的DP循环,后面DFS,所以很多书上说n^2 n^3严格都不对,因为要返回所有解,最坏指数个呢

class Solution {
public:
    vector<string> cur;
    vector< vector<string> > ans;
    bool dp[10000];
    int n;
    string s_;
    bool isPa(string s)
    {
        int n=s.size();
        for(int i=0, j=n-1; i<j;i++, j--)
            if(s[i]!=s[j]) return 0;
        return 1;
    }
    vector<vector<string>> partition(string s) {
        n=s.size();s_=s;
        memset(dp,0,sizeof(dp));
        dp[0]=1;
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<i;j++)
            {
                if(dp[j] && isPa(s.substr(j, i-j)))
                {
                    dp[i]=1;
                    break;
                }
            }
        }
        ans.clear();cur.clear();
        dfs(n);
        return ans;
    }
    void dfs(int i)
    {
        if(i<=0)
        {
            ans.push_back(cur);
            reverse(ans.back().begin(), ans.back().end());
        }
        else
        {
            for(int j=0;j<i;j++)
            {
                if(dp[j] && isPa(s_.substr(j, i-j)))
                {
                    cur.push_back(s_.substr(j, i-j));
                    dfs(j);
                    cur.pop_back();
                }
            }
        }
    }
};

两方的算法,状态增加一维

class Solution {
public:
    bool dp[1500][1500];
    vector<string> cur;
    vector<vector<string> > ans;
    string s_;
    vector<vector<string>> partition(string s) {
        int n=s.size();s_=s;
        for(int i=0;i<n;i++) dp[i][i]=1;
        for(int i=0;i<n-1;i++) dp[i][i+1]=s[i]==s[i+1];
        for(int len=3;len<=n;len++)
        {
            for(int i=0;i<n;i++)
            {
                int j=i+len-1;
                if(j>=n) continue;
                dp[i][j]=dp[i+1][j-1] && s[i]==s[j];
            }
        }
        cur.clear();ans.clear();
        dfs(0,n-1);
        return ans;
    }
    void dfs(int s, int e)
    {
        if(s>e) ans.push_back(cur);
        for(int i=s;i<=e;i++)
        {
            if( dp[s][i] )
            {
                cur.push_back(s_.substr(s,i-s+1));
                dfs(i+1, e);
                cur.pop_back();
            }
        }
    }
};

Posted by richard爱闹 - 12月 26 2014
如需转载,请注明: 本文来自 Richard