代码随想录算法训练营第二十七天|39. 组合总和、40.组合总和II、131.分割回文串
- 39. 组合总和
- 40.组合总和II
- 131.分割回文串
题解参考y总的:http://www.acwing.com
39. 组合总和
我是一看就会,一写就废。先看代码:
class Solution {
public:vector<vector<int>> res;vector<int> path;vector<vector<int>> combinationSum(vector<int>& candidates, int target) {dfs(candidates,target,0);return res;}void dfs(vector<int>& candidates,int target,int n){if(target == 0){res.push_back(path);return;}if(n == candidates.size()) return;for(int i = 0;candidates[n] * i <= target;i++){dfs(candidates,target - candidates[n]*i,n + 1);path.push_back(candidates[n]);}for(int i = 0;candidates[n] * i <= target;i++){path.pop_back();}}
};
这道题目使用模板,关键在两点,一个是递归结束条件,一个是递归过程。首先是递归的结束条件,递归的结束条件就是和前面组合||一样,当target为0的时候,就结束。然后递归的过程,因为这里每个的元素是无限重复选取的,所以使用 i * candidates[n],这里n代表下标为n的元素。
40.组合总和II
看代码:
class Solution {
public:vector<vector<int>> res;vector<int> path;vector<vector<int>> combinationSum2(vector<int>& c, int target) {sort(c.begin(),c.end());dfs(c,target,0);return res;}void dfs(vector<int>& c,int target,int n){if(!target){res.push_back(path);return;}if(n == c.size()) return;int k = n + 1;while(k < c.size() && c[k] == c[n]) k++;int cnt = k - n;for(int i = 0;c[n] * i <= target && i <= cnt;i++){dfs(c,target - c[n] * i,k);path.push_back(c[n]);}for(int i = 0;c[n] * i <= target && i <= cnt;i++){path.pop_back();}}
};
和第一个一样,只不过这里的元素变成有限个数了,所以首先要统计元素个数,这里借鉴的y的,先从小到大排个序,然后统计一个元素重复的个数,还是使用candidates[n] * i,只要i不超过限制的个数就可以。还可以使用hash表来记录元素的个数。
131.分割回文串
看代码:
class Solution {
public:vector<vector<bool>> f;vector<vector<string>> res;vector<string> path;vector<vector<string>> partition(string s) {int n = s.size();f = vector<vector<bool>>(n,vector<bool>(n));for(int j = 0;j < n;j++){for(int i = 0;i <= j;i++){if(i ==j) f[i][j] = true;else if(s[i] == s[j]){if(i + 1 > j - 1 || f[i + 1][j - 1]) f[i][j] = true;}}}dfs(s,0);return res;}void dfs(string s,int n){if(n == s.size()) res.push_back(path);else{for(int i = n;i < s.size();i++){if(f[n][i]){path.push_back(s.substr(n,i - n + 1));dfs(s,i + 1);path.pop_back();}}}}
};
首先要判断一个字串是不是回文,首先第一种方法可以使用双指针的方法,一个从头开始,一个从尾开始,只要每次向中间走都字符都相同,那就是回文。
还有一种方法是:(下图来自acwing截图)
首先判断i到j串,第i位和第j位是相同的字符,然后第i+1和第j-1也是回文,这就用的是递归,第一个方法用的是迭代。
f就是用来记录从第i到j位是否是回文串的。首先先构造f,如果是i==j,也就是单个字母,单个字母肯定的是回文;然后判断s[i]和s[j]是否相等,如果相等,再判断一下,如果是i + 1 > j - 1(也就是就只有两个字母),因为之前已经判断了s[i] == s[j]了,所以就两个字母肯定是回文;或者f[i + 1][j - 1]也是回文,那i到j就是回文。
构建好回文记录表f之后,开始走模板第一步,判断条件,什么时候才算一轮的结果,那必须是所有字母都走过一遍的时候,才是加入一个path的时候,然后然后i从n开始,i每次向后走,每次都判断一下n到i是否是回文,如果是的话就加入答案中。
回到一个地方:有人可能注意到,递归的时候dfs后面是i+1,而不是n+1,我个人认为是如果是n+1的话,因该就已有一条路径了,因为每次递归下去,再回溯到开始的时候,就是最开始的n,它就不变了,最后答案中估计也就一条正确答案,比如aab,可能答案只有:[[“a”,“a”,“b”]]其他没有了,因为回溯到开始n不变了。也就是说如果是n+1,外面的for循环相当于没有作用。