回溯算法中的判断是指如回文串、IP地址这类题目,需要对划分的子集(子字符串,子数字)进行一个是否有效的判断。当子符串都为回文串、满足IP规则才有效。这和之前组合总和有区别,组合总和的判断相对简单,只要判断总和是否为target值即可。
-
回文串
void backtracking(const string& s, int startIndex) { if (startIndex >= s.size()) { result.push_back(path); return ; } for (int i = startIndex; i < s.size(); i++) { if (isPalindrome(s, startIndex, i)) { string str = s.substr(startIndex, i - startIndex + 1); path.push_back(str); backtracking(s, i + 1); path.pop_back(); } else { continue; } } } vector<vector<string>> partition(string s) { backtracking(s, 0); return result; }
这里需要思考的一个点,就是程序如何保证每一个划分的子串都是符合要求的,因此,需要在将某个节点的值放进path之前做判断,如上述代码中,for 语句中添加了判断if (isPalindrome(s, startIndex, i)),只有当,当前子串为回文串时,才将其放入,否则,continue。注意,是continue,而不是break.因为有可能'ab',不是回文,但是下一个子串'aba'是回文串。
-
IP地址 IP地址难点在于如何设计逻辑,确保分割的数字都是符合要求的。这里思考是否可以结合回文串的逻辑框架。答案是可以的。代码如下
void backtracking(string& s, int startIndex, int count) { if (count == 3) { if (isValid(s, startIndex, s.size() - 1)) { result.push_back(s); } return ; } for (int i = startIndex; i < s.size(); i++) { if (isValid(s, startIndex, i)) { s.insert(s.begin() + i + 1, '.'); count++; backtracking(s, i + 2, count); count--; s.erase(s.begin() + i + 1); } else break; }} bool isValid(const string& s, int start, int end) { if (start > end) { return false; } if (s[start] == '0' && start != end) { return false; } int num = 0; for (int i = start; i <= end; i++) { if (s[i] > '9' || s[i] < '0') { return false; } num = num * 10 + (s[i] - '0'); if (num > 255) { return false; } } return true; } vector<string> restoreIpAddresses(string s) { backtracking(s, 0, 0); return result; }
大体的逻辑就是在for中加了一个if else 的判断。有些细节上的区别,IP地址是直接break.这个比较好理解,结合IP地址的规则,如果当前子串已经违规那么再往后多加一位也一定违规,因此,直接break。这里的子字符串是直接在S上进行操作,S.insert(),和s.erase()的操作,而不是新建一个子字符串。判断isValid()的函数比较复杂,规则比较多。
- 子集问题 子集问题,搜集叶子节点,和路径有所不同,子集问题是将出现过的叶子节点搜集起来。当没有重复元素时比较简单,有重复节点该怎么操作?避免结果中出现重复的组合。