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.
http://blog.csdn.net/yutianzuijin/article/details/16850031
定义状态 f(i,j) 表示区间 [i,j] 之间最小的 cut 数,则状态转移方程为
f(i, j) = min { f(i, k) + f(k + 1, j) } , i <=k <= j, 0 < i <= j <= n
我们求解的是整个字符串的最小cut数,可以从字符串的头开始,也可以从尾部开始
原问题的目的是求一个最小的划分,如果利用count矩阵则会求从位置i开始的每一个可能划分,实际上这是在做无用功。原因就在于f矩阵的第二维为划分个数,这一维对求最小值问题没有意义,我们可以去掉。所以我们重新定义一个新的一维count数组,用来表示从位置i开始到最后的最小划分个数。这时有公式:
公式的含义是,从i开始的字符串的最小划分为:如果从位置i到位置j的子串是回文串,则从i开始的划分可以通过将i到j的子串看作划分的一部分,然后加上从j+1位置开始的子串最小划分,并选择可能情况中的最小值即为从i开始的最小划分。
所以可以分两步求解:
1. 计算 p[i][j],表示s[i][j]是否是回文字串,
2 计算f[i],也就是count[i]
class Solution { public: int minCut(string s) { const int n = s.size(); bool p[n][n]; // whether s[i,j] is palindrome fill_n(&p[0][0], n * n, false); for (int i = n - 1; i >= 0; --i) for (int j = i; j < n; ++j) p[i][j] = s[i] == s[j] && ((j - i < 2) || p[i + 1][j - 1]); int f[n]; // s[i,n-1] has cut number for (int i = 0; i < n; ++i) { f[i] = n-1-i; } for (int i = n - 1; i >= 0; --i) { for (int j = i; j < n; ++j) { if(p[i][j]) { if(j == n-1) f[i] = 0; else f[i] = min(f[j+1]+1, f[i]); } } } #if 0 for (int i = 0; i < n; ++i) { cout << f[i] << " "; } cout << endl; #endif return f[0]; } };
转一个,更加紧凑的code
// LeetCode, Palindrome Partitioning II // 时间复杂度 O(n^2),空间复杂度 O(n^2) class Solution { public: int minCut(string s) { const int n = s.size(); int f[n+1]; bool p[n][n]; fill_n(&p[0][0], n * n, false); //the worst case is cutting by each char for (int i = 0; i <= n; i++) f[i] = n - 1 - i; // 最后一个 f[n]=-1 for (int i = n - 1; i >= 0; i--) { for (int j = i; j < n; j++) { if (s[i] == s[j] && (j - i < 2 || p[i + 1][j - 1])) { p[i][j] = true; f[i] = min(f[i], f[j + 1] + 1); } } } return f[0]; } };