Palindrome Partitioning
Given a string s, partition s such that every substring of the partition is a palindrome.
Return all possible palindrome partitioning of s.
For example, given s = "aab"
,
Return
[ ["aa","b"], ["a","a","b"] ]
方法一:暴力破解,直接深度搜索字串,直到找到所有解为止。假设字符串为S=a0a1a2a3a4a5...an,那么从下标0开始往后找回文字串,假设下标为i时有S[0...i]是回文,则将原问题化为子问题,即在S[i+1...n]中继续从头往后寻找回文字串,详细请看代码:
1 class Solution { 2 public: 3 vector<vector<string>> partition(string s) { 4 ans.clear(); 5 vector<string> vstrs; 6 dfs(vstrs, s); 7 return ans; 8 } 9 10 private: 11 void dfs(vector<string>& vstrs, string s) { //深度搜索 12 if( s.empty() ) { //如果s为空串,则说明前面的切割出的字串均为回文 13 ans.push_back(vstrs); 14 return ; 15 } 16 for(int i=1; i<=s.length(); ++i) { //依次从前往后开始切割 17 string tstr = s.substr(0, i); 18 if( ispalindrome(tstr) ) { //如果被切割出的字符串是回文 19 vstrs.push_back(tstr); //存放结果 20 dfs(vstrs, s.substr(i, s.length()-i)); //继续深搜未被切割的后面的字串 21 vstrs.pop_back(); //pop结果 22 } 23 } 24 } 25 26 //判断是否是回文 27 bool ispalindrome(const string& s) { 28 for(int i=0, j=s.length()-1; i<j; ++i,--j) 29 if( s[i] != s[j] ) return false; 30 return true; 31 } 32 33 private: 34 vector< vector<string> > ans; //保存切割后的结果 35 };
方法二:转为dp问题,另dp[i][j] 以 i 为起点,长度为 j 的的字串,则有
当 s[i] == s[i+j-1] 且 dp[i+1][j-2] == true 时 dp[i][j] = true;否则为false
当 j = 1 时 dp[i][j] = true;
最后通过dp二维数组,依然深度搜索字串,得出最后的结果,代码如下:
1 class Solution { 2 public: 3 vector<vector<string>> partition(string s) { 4 if( s.empty() ) 5 return vector< vector<string> >(); 6 vector< vector<bool> > dp(s.length(), vector<bool>(s.length()+1, false)); //行代表子串起始坐标,列代表字串长度 7 for(int j=0; j<2; ++j) //长度为0,1都认为是回文 8 for(int i=0; i<s.length(); ++i) 9 dp[i][j] = true; 10 for(int j=2; j<=s.length(); ++j) //从长度为2开始计算 11 for(int i=0; i<=s.length()-j; ++i) 12 dp[i][j] = dp[i+1][j-2] && (s[i] == s[i+j-1]); 13 ans.clear(); 14 vector<string> vstrs; 15 dfs(dp, vstrs, s, 0, s.length()); //深搜dp,找出满足条件的结果 16 return ans; 17 } 18 19 //k为子串起始下标 20 void dfs(vector<vector<bool> >& dp, vector<string>& vstrs, string& s, int k, int n) { 21 if( k >= n ) { 22 ans.push_back(vstrs); 23 return ; 24 } 25 int len = n-k; //子串[k...n-1]的长度 26 for(int i=1; i<=len; ++i) { //长度为1开始遍历 27 if( dp[k][i] ) { //如果字串s[k...k+i-1]是回文 28 vstrs.push_back(s.substr(k,i)); 29 dfs(dp, vstrs, s, k+i, n); 30 vstrs.pop_back(); 31 } 32 } 33 } 34 35 private: 36 vector< vector<string> > ans; //保存切割后的结果 37 };
Palindrome Partitioning II
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.
方法一:依然使用I所用的方法,直接超时,不能过大数据,代码如下:
1 class Solution { 2 public: 3 int minCut(string s) { 4 if( s.empty() ) 5 return 0; 6 vector< vector<bool> > dp(s.length(), vector<bool>(s.length()+1, false)); //行代表子串起始坐标,列代表字串长度 7 for(int j=0; j<2; ++j) //长度为0,1都认为是回文 8 for(int i=0; i<s.length(); ++i) 9 dp[i][j] = true; 10 for(int j=2; j<=s.length(); ++j) //从长度为2开始计算 11 for(int i=0; i<=s.length()-j; ++i) 12 dp[i][j] = dp[i+1][j-2] && (s[i] == s[i+j-1]); 13 ans = 0x3fffffff; 14 dfs(dp, 0, s.length(), 0); //深搜dp,找出满足条件的结果 15 return ans; 16 } 17 18 //k为子串起始下标 19 void dfs(vector<vector<bool> >& dp, int k, int n, int mincut) { 20 if( k >= n ) { 21 ans = min(ans, mincut-1); 22 return ; 23 } 24 int len = n-k; //子串[k...n-1]的长度 25 for(int i=1; i<=len; ++i) { //长度为1开始遍历 26 if( dp[k][i] ) { //如果字串s[k...k+i-1]是回文 27 dfs(dp, k+i, n, mincut+1); 28 } 29 } 30 } 31 32 private: 33 int ans; //保存切割后的结果 34 };
方法二:首先求出dp[i][j],即从i开始长度为j的子串是否是回文。
接着假设mincuts[i]表示s[0...i]字串最小切割数可让子串均为回文,那么有
若s[0...i]是回文,则mincuts[i] = 0;
若s[0...i]不是回文,则mincuts[i] = min{ s[k+1...i]是回文 ? mincuts[k]+1 : mincuts[k] + i-k } k=0,1,2,3...i-1
初始化:mincuts[0] = 0,时间复杂度为O(n2) + O(n2) = O(n2),代码如下:
1 class Solution { 2 public: 3 int minCut(string s) { 4 if( s.empty() ) return 0; 5 vector< vector<bool> > dp( s.length(), vector<bool>(s.length()+1, false) ); 6 for(int i=0; i<s.length(); ++i) //长度为0,1的子串均认为是回文 7 dp[i][0] = dp[i][1] = true; 8 for(int j=2; j<=s.length(); ++j) //计算各字串的回文情况 9 for(int i=0; i<=s.length()-j; ++i) 10 dp[i][j] = dp[i+1][j-2] && (s[i]==s[i+j-1]); 11 vector<int> mincuts(s.length(), 0); //初始化为0 12 for(int i=1; i<s.length(); ++i) { 13 if( dp[0][i+1] ) mincuts[i] = 0; //如果s[0...i]是回文 14 else { //循环遍历k,找出最小的切割数 15 int tmin = 0x3fffffff; 16 for(int k=0; k<i; ++k) 17 if( dp[k+1][i-k] ) tmin = min(tmin, mincuts[k]+1); //如果s[k+1...i]是回文,则为mincuts{k]+1 18 else tmin = min(tmin, mincuts[k]+i-k); //不是,则为mincuts[k]+i-k 19 mincuts[i] = tmin; 20 } 21 } 22 return mincuts[s.length()-1]; 23 } 24 };