每到周五就想放松么,还是写两个题解吧。今天写了几个问题,发现都是动态规划的问题。就说这两个吧。
题1:
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"] ]
找出所有的回文子串,如果是判断一个串是否是回文串,我想大家都应该能想出来。一般的判断一个串是否为回文串,直接从两头往中间找就可以了。这题的要求是让我们把这个字符串的所有子串都判断一遍。显然枚举每个子串的时间复杂度是O(n*n)的,加上判断的,算法时间复杂度是O(n*n*n)。同时判断完成时,还需要让原来的串划分为不同的回文串。这是个回溯,或者说dfs,因为要回复原来的状态,所要要回溯。
其实这是一个典型的动态规划问题,写这个的动态规划写法有几种。可以考虑从右往左,也可以从左往右。我在这里就写一个根据步长的。首先定义一个bool数组dp[i][j],表示从i到j是否是一个回文串。
状态转移,当dp[i+1][j-1]==1 &&s[i]==s[j] 或者只有一个元素 那么dp[i][j]=1;
步长++,就可以求出所有子串的状态。
有了第一步基础。下面的问题就变成了,搜索问题。很明显是一个深搜,出口条件是找到字符串最后位置。注意下回溯就好了。
class Solution { public: vector<vector<string>> ss; bool dp[1500][1500]; vector<vector<string>> partition(string s) { // IMPORTANT: Please reset any member data you declared, as // the same Solution instance will be reused for each test case. int n=s.size(); if(s.size() == 0) return vector<vector<string>>(); for(int i=0;i<n;i++) { for(int j=0;j<n;j++) dp[i][j]=false; dp[i][i]=true; } for(int k=1;k<n;k++) { for(int i=0;i<n-k;i++) { if(k==1 && s[i]==s[i+k]) dp[i][i+k]=true; else if(dp[i+1][i+k-1] &&s[i]==s[i+k]) dp[i][i+k]=true; } } vector<string> tem; dfs(0,n,s,tem); return ss; } void dfs(int j,int n,const string &str,vector<string> &cur) { if(j==n) { ss.push_back(cur); return ; } for(int k=j;k<n;k++) { if(dp[j][k]) { string t(str.begin()+j,str.begin()+k+1); cur.push_back(t); dfs(k+1,n,str,cur); cur.pop_back(); } } } };
第二题,mark下 明天早上来写吧。。。。。教研室关门了。。。。。。。
教研室发了好多电影票,大家都去看电影啦,,我对电影没有兴趣,,还是继续写完吧。。。。。
题目2:
在要求几次分割可以将一个串分割为回文串。其实就是求一个串最少能用几个回文串表示嘛。所以我们求最少能用几个回文串表示就可以了。
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.
之所以超时,是因为没有办法有效利用前面信息,因为我是按步长求的,所以不能得到前面串的回文信息。所以这次我用从左往右的dp,可以利用前面求得的最少回文串。
思想就是从左往右开始,j=1;j++。。定义一个f[n]保存从0到i位置,最少回文串,初始时,f[i]=i+1;
求回文串的dp;
for(int j=1;j<n;j++) { for(int i=j;i>=0;i--) { if(s[i]==s[j] && (j-i<2||dp[i+1][j-1])) dp[i][j]=true; } }j 从左往右遍历,每次i--就可以求得0,j所有子串是否为回文串。。更新f状态也比较简单,当dp[i][j]==true的时候,我们需要更新我们的f,状态转移f[j]=min(f[i-1]+1,f[j]);
int minCut(string s) { // IMPORTANT: Please reset any member data you declared, as // the same Solution instance will be reused for each test case. int n=s.size(); vector<vector<bool> > dp(n,vector<bool>(n,false)); for(int i=0;i<n;i++) dp[i][i]=true; int f[n]; for(int i=0;i<n;i++) f[i]=i+1; for(int j=1;j<n;j++) { for(int i=j;i>=0;i--) { if(s[i]==s[j]&&(j-i<2||dp[i+1][j-1])) { dp[i][j]=true; if(i==0) f[j]=min(f[j],1); else f[j]=min(f[j],f[i-1]+1); } } } return f[n-1]-1; }
写的匆忙,如果有不对的地方请大家多多指正。