算法训练 day27|回溯算法part03

39. 组合总和

需要startIndex的情况:

一个集合求组合需要startIndex来控制for循环起始位置

多个集合取组合不需要startInex控制,各个集合独立,互不影响

重复选取元素的方法:backtrack 直接回溯当前位置,相当于可以选择当前元素或者之后的元素( // 不用i+1了,表示可以重复读取当前的数)

剪枝:一般在循环中进行剪枝

如果已经比目标更大,就不需要接着循环。

(结束条件中也有类似剪枝e.g sum>target return;但是循环仍要进行,还会backtrack到下一层效率不如直接在循环中剪枝高)

40.组合总和II

去重的重复选取有两类:

1. 同一树枝上的重复选取

2. 同一树层上的重复选取

本题去重是去去除同一树层的重复选取,如[1,2(1),2 (2),2(3),4,5]中和为11的子集有[2,4,5] [2(第二个2),4,5]重复属于第二类重复

第一类重复时子集[2(1),2 (2),2(3)]相当于是用了三个不同的2,是树枝上的重复

树层去重,需要对数组排序

图源:代码随想录

算法训练 day27|回溯算法part03_第1张图片

  • used[i - 1] == true,说明同一树枝candidates[i - 1]使用过
  • used[i - 1] == false,说明同一树层candidates[i - 1]使用过

同一树层,used[i - 1] == false 才能表示,当前取的 candidates[i] 是从 candidates[i - 1] 回溯而来的。

而 used[i - 1] == true,说明是进入下一层递归,去下一个数,所以是树枝上

算法训练 day27|回溯算法part03_第2张图片

也可以使用startIndex进行去重:

    // 要对同一树层使用过的元素进行跳过
    if (i > startIndex && candidates[i] == candidates[i - 1]) {
        continue;
    }

131.分割回文串

两个问题:

1.切割问题:用回溯遍历不同的切割方式:

图源:代码随想录

算法训练 day27|回溯算法part03_第3张图片

如果切割出来的字串不是回文串,直接略过,只有所有子串都是回文串时,切割线才会到字符串最后,此时结束,保存结果到结果集,返回

startIndex就可以作为切割线:是下一个子串开始的位置,所以当startIndex==s.length()时,就时结束的条件达成的时候

2. 如何判断回文串:双指针/dp+memo

优化:对于回文串判断的优化:每次查询回文,会有重复计算,可以利用一下规律减少计算:

给定一个字符串s, 长度为n, 它成为回文字串的充分必要条件是s[0] == s[n-1]s[1:n-1]是回文字串。

其实就是动态规划的方法:我们可以高效地事先一次性计算出, 针对一个字符串s, 它的任何子串是否是回文字串, 然后在我们的回溯函数中直接查询即可, 省去了双指针移动判定这一步骤. 

本题难点

  • 切割问题可以抽象为组合问题
  • 如何模拟那些切割线 (把字符串存入path)
  • 切割问题中递归如何终止 (startIndex作为切割线达到字符串尾部)
  • 在递归循环中如何截取子串(截取i到startIndex的字符串)
  • 如何判断回文(双指针/dp)

你可能感兴趣的:(算法)