leetcode 132 分割回文串II(dp)
题目描述:
题解:一开始的时候想着用区间dp。dp[i][j]表示s[i~j]分割为会文子串的最少分割次数。状态转移也很简单,要么s[i~j]本身是回文串不需要分割;
要么枚举分隔点K,dp[i][j] = min(dp[i][j],dp[i][k]+dp[k+1][j]+1);代码如下:
class Solution { public: int minCut(string s) { int Len = s.length(); int dp[Len][Len]; for(int i=0;i<Len;i++) { for(int j=0;j<Len;j++) dp[i][j] = INT_MAX; } for(int i=0;i<Len;i++) dp[i][i] = 0; for(int l = 2;l<=Len;l++) { for(int i = 0;i+l-1<Len;i++) { int j = i+l-1; if(s[i] == s[j]) { if(l == 2 || dp[i+1][j-1] == 0) { // cout <<i <<" " << j <<endl; dp[i][j] = 0; continue; } } for(int k = i;k<j;k++) { dp[i][j] = min(dp[i][j],dp[i][k]+dp[k+1][j]+1); } } } return dp[0][Len-1]; } };
但是这么做的话时间复杂度为o(n^3),果不其然超时了。可以改进的地方有两点:
1. 枚举K的时候,不需要遍历所有情况,只需要枚举s[i~k]或者s[k+1~j]有一个为回文串的情况。
2. 不需要用区间dp的板子,二维的dp三维的遍历,时间复杂度太高了。
新定义一个dp:dp[i]表示s[0~i]分割为回文子串需要的最少分割次数。状态转移结合改进点1进行处理就好。
值得提一下的时候这里判断一个子串是不是回文子串,可以预先用一个二维的dp处理掉。预处理的dp我借鉴了一下别人的代码,自己想的状态转移又是O(n^3)的.....
AC代码:
class Solution { public: int minCut(string s) { int Len = s.length(); int judge[Len][Len]; for(int i = 0;i<Len;i++) { for(int j=0;j<Len;j++) judge[i][j] = 0; }
// 判断s[left][right]是不是回文串,由于judge[left][right]需要根据judge[left+1][right-1]转移
// 那么在遍历的时候,第一维遍历right,这样就能保证有序的进行状态转移 for(int right = 0;right<Len;right++) { for(int left = 0;left<=right;left++) { if(left == right) judge[left][right] = 1; else { if(right - left == 1) { if(s[left] == s[right]) judge[left][right] = 1; } else { if(s[left] == s[right]) judge[left][right] = judge[left+1][right-1]; } } } } //cout << judge[0][2] <<endl; int dp[Len]; dp[0] = 0; for(int i=1;i<Len;i++) dp[i] = INT_MAX; for(int i=1;i<Len;i++) { if(judge[0][i] == 1) { dp[i] = 0; continue; } for(int j=0;j<i;j++) { if(judge[j+1][i] == 1) { dp[i] = min(dp[i],dp[j]+1); } } } return dp[Len-1]; } };