LeetCode Backtracking

Problems Lists

What is Backtracking?

Medium

357. Count Numbers with Unique Digits

Description
Given a non-negative integer n, count all numbers with unique digits, x, where $0 \le x < 10^n$.

Example:
Given n = 2, return 91. (The answer should be the total numbers in the range of $0 \le x < 100$, excluding [11,22,33,44,55,66,77,88,99])

Credits:
Special thanks to @memoryless for adding this problem and creating all test cases.

解题思路:
一个n位数并且每位数字不重复,总共有这么多个数字

然后累加。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution {
public:
int countNumbersWithUniqueDigits(int n) {
if (n < 0) return 0;
if (n > 10) n = 10;
int result = 0;
int tmp1 = 1;
int tmp2 = 0;
for (int i = 0; i <= n; i++) {
result += tmp1 - tmp2;
tmp1 *= 10 - i;
if (i == 0) {
tmp2 = 1;
}
else {
tmp2 *= 10 - i;
}
}
return result;
}
};

回溯方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Solution {
public:
int countNumbersWithUniqueDigits(int n) {
int res = 1, max = pow(10, n), used = 0;
for (int i = 1; i < 10; ++i) {
used |= (1 << i);
res += search(i, max, used);
used &= ~(1 << i);
}
return res;
}
int search(int pre, int max, int used) {
int res = 0;
if (pre < max) ++res;
else return res;
for (int i = 0; i < 10; ++i) {
if (!(used & (1 << i))) {
used |= (1 << i);
int cur = 10 * pre + i;
res += search(cur, max, used);
used &= ~(1 << i);
}
}
return res;
}
};

bug版回溯

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Solution {
public:
int countNumbersWithUniqueDigits(int n) {
if (n > 10) {
return countNumbersWithUniqueDigits(10);
}
vector<bool> used(10, false);
int count = 1;
long max = pow(10, n);
for (int i = 0; i < 10; i++) {
used[i] = true;
count += search(i, max, used);
used[i] = false;
}
return count;
}
int search(long pre, long max, vector<bool>& used) {
int count = 0;
if (pre < max) count++;
else return count;
for (int i = 0; i < 10; i++) {
if (!used[i]) {
used[i] = true;
long cur = 10 * pre + i;
count += search(cur, max, used);
used[i] = false;
}
}
return count;
}
};


22. Generate Parentheses

Description
Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses.

For example, given n = 3, a solution set is:

1
2
3
4
5
6
7
[
"((()))",
"(()())",
"(())()",
"()(())",
"()()()"
]

解题思路:合法字符串的任意位置,右括号数量不大于左括号数量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Solution {
public:
vector<string> generateParenthesis(int n) {
vector<string> res;
func(n, n, "", res);
return res;
}
void func(int left, int right, string tmp, vector<string> &res) {
if (left > right) return;
if (left ==0 && right == 0) res.push_back(tmp);
if (left > 0) func(left - 1, right, tmp + '(', res);
if (right > 0) func(left, right - 1, tmp + ')', res);
}
};

参考来源


216. Combination Sum III

Description:

Find all possible combinations of k numbers that add up to a number n, given that only numbers from 1 to 9 can be used and each combination should be a unique set of numbers.

Example 1:

Input: k = 3, n = 7

Output:

1
[[1,2,4]]

Example 2:

Input: k = 3, n = 9

Output:

1
[[1,2,6], [1,3,5], [2,3,4]]

Credits:
Special thanks to @mithmatt for adding this problem and creating all test cases.

解题思路:把已经算过的数放入一个向量中,在不满足条件的情况下,把值退出来;满足条件下,把最终结果保存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
public:
vector<vector<int>> combinationSum3(int k, int n) {
vector<vector<int>> res;
vector<int> tmp;
func(k, n, 1, tmp, res);
return res;
}
void func(int k, int n, int level, vector<int> &tmp, vector<vector<int>> &res) {
if (n < 0) return;
if (n == 0 && tmp.size() == k) res.push_back(tmp);
for (int i = level; i <= 9; i++) {
tmp.push_back(i);
func(k, n - i, i + 1, tmp, res);
//func(k, n - i, level + 1, tmp, res);//bug版本;也能输出,但是会出现重复数字
tmp.pop_back();
}
}
};

参考


46. Permutations

Description:

Given a collection of distinct numbers, return all possible permutations.

For example,
[1,2,3] have the following permutations:

1
2
3
4
5
6
7
8
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]

!!!这是宝宝写出来的第一个回溯算法题!!!
解题思路:大循环【从nums中擦除一个数,在tmp中加入,然后把nums和tmp放到下一层递归;终止条件是当nums==0,tmp的大小和最初nums一样大时,查找tmp是否在结果中,如果在跳出,否则加入结果;然后把擦除的数插入回nums,tmp弹出这个数】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public:
vector<vector<int>> permute(vector<int>& nums) {
vector<vector<int>> res;
vector<int> tmp;
func(nums, tmp, res);
return res;
}
void func(vector<int>& nums, vector<int>& tmp, vector<vector<int>>& res) {
if (nums.size() == 0 && find(res.begin(), res.end(), tmp) != res.end()) return;
if (nums.size() == 0 && find(res.begin(), res.end(), tmp) == res.end()) res.push_back(tmp);
for (auto iter = nums.begin(); iter != nums.end(); iter++) {
int val = *iter;
tmp.push_back(val);
nums.erase(iter);
func(nums, tmp, res);
nums.insert(iter, val);
tmp.pop_back();
}
}
};

别人的解


89. Gray Code

Description:

The gray code is a binary numeral system where two successive values differ in only one bit.

Given a non-negative integer n representing the total number of bits in the code, print the sequence of gray code. A gray code sequence must begin with 0.

For example, given n = 2, return [0,1,3,2]. Its gray code sequence is:

1
2
3
4
00 - 0
01 - 1
11 - 3
10 - 2

Note:
For a given n, a gray code sequence is not uniquely defined.

For example, [0,2,3,1] is also a valid gray code sequence according to the above definition.

For now, the judge is able to judge based on one instance of gray code sequence. Sorry about that.

公式法:每个格雷码 = 当前二进制数 异或 (当前二进制数右移一位)

1
2
3
4
5
6
7
8
9
10
class Solution {
public:
vector<int> grayCode(int n) {
vector<int> res;
for (int i = 0; i < pow(2, n); i++) {
res.push_back((i >> 1) ^ i);
}
return res;
}
};

镜像翻转法:
image

1
2
3
4
5
6
7
8
9
10
11
12
13
class Solution {
public:
vector<int> grayCode(int n) {
vector<int> res;
res.push_back(0);
for (int i = 0; i < n; i++) {
for (int j = pow(2, i) - 1; j >= 0; j--) {
res.push_back(res[j] | (1 << i));
}
}
return res;
}
};

回溯法,目前没搞明白……囧

回溯法1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution {
public:
vector<int> grayCode(int n) {
vector<int> res;
unordered_set<int> s;
helper(n, s, 0, res);
return res;
}
void helper(int n, unordered_set<int>& s, int out, vector<int>& res) {
if (!s.count(out)) {
s.insert(out);
res.push_back(out);
}
for (int i = 0; i < n; ++i) {
int t = out;
if ((t & (1 << i)) == 0) t |= (1 << i);
else t &= ~(1 << i);
if (s.count(t)) continue;
helper(n, s, t, res);
break;
}
}
};

回溯法2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
int conversion(int n)
{
int temp=1;
if(n==1)
return 1;
for(int i=1;i<n;i++)
temp=temp*2;
return temp;
}
vector<int> grayCode(int n) {
vector<int> result;
if(n==0)
{
result.push_back(0);
return result;
}
if(n==1)
{
result.push_back(0);
result.push_back(1);
return result;
}
else
{
//queue<int> temp;
result=grayCode(n-1);
int len=result.size();
for(int i=len-1;i>=0;--i)
{
int temp=result[i]+conversion(n);
result.push_back(temp);
}
return result;
}
}


78. Subsets

Description:

Given a set of distinct integers, nums, return all possible subsets.

Note: The solution set must not contain duplicate subsets.

For example,
If nums = [1,2,3], a solution is:

1
2
3
4
5
6
7
8
9
10
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]

解题思路

1
2
3
4
5
6
7
8
9
10
[]
/ \
/ \
/ \
[1] []
/ \ / \
/ \ / \
[1 2] [1] [2] []
/ \ / \ / \ / \
[1 2 3] [1 2] [1 3] [1] [2 3] [2] [3] []

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution {
public:
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector<int>> res;
vector<int> tmp = {};
func(0, tmp, res, nums);
return res;
}
void func(int pos, vector<int> &tmp, vector<vector<int>> &res, vector<int> &nums) {
res.push_back(tmp);
for (int i = pos; i < nums.size(); i ++) {
tmp.push_back(nums[i]);
func(i + 1, tmp, res, nums);
tmp.pop_back();
}
}
};

77. Combinations

Description:
Given two integers n and k, return all possible combinations of k numbers out of 1 … n.

For example,
If n = 4 and k = 2, a solution is:

1
2
3
4
5
6
7
8
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]

解题思路:
这题是组合情况,比之前的46题排列情况更简单一点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Solution {
public:
vector<vector<int>> combine(int n, int k) {
vector<vector<int>> res;
vector<int> input;
vector<int> tmp;
for (int i = 1; i <= n; i++) {
input.push_back(i);
}
func(n, k, tmp, 0, res);
return res;
}
void func(int n, int k, vector<int> &tmp, int pos, vector<vector<int>> &res) {
if (tmp.size() == k && find(res.begin(), res.end(), tmp) == res.end()) res.push_back(tmp);
else if (tmp.size() == k) return;
for (int i = pos; i < n; i++) {
tmp.push_back(i + 1);
func(n, k, tmp, i + 1, res);
tmp.pop_back();
}
}
};

39. Combination Sum

Description:

Given a set of candidate numbers (C) (without duplicates) and a target number (T), find all unique combinations in C where the candidate numbers sums to T.

The same repeated number may be chosen from C unlimited number of times.

Note:
All numbers (including target) will be positive integers.
The solution set must not contain duplicate combinations.
For example, given candidate set [2, 3, 6, 7] and target 7,
A solution set is:

1
2
3
4
[
[7],
[2, 2, 3]
]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Solution {
public:
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
vector<vector<int>> res;
vector<int> tmp;
//int sum = 0;
func(target, tmp, res, candidates);
return res;
}
void func(int target, vector<int>& tmp, vector<vector<int>>& res, vector<int>& candidates) {
int sum = 0;
for (auto c: tmp) sum += c;
if (sum > target) return;
else if (sum == target) {
vector<int> tmp2 =tmp; //一开始没有加tmp2变量,导致每次排序之后tmp.pop_back()改变原先内容
sort(tmp2.begin(), tmp2.end());
if (find(res.begin(), res.end(), tmp2) == res.end()) res.push_back(tmp2);
else return;
}
for (int i = 0; i < candidates.size(); i++) {
tmp.push_back(candidates[i]);
func(target, tmp, res, candidates);
tmp.pop_back();
}
}
};

faster

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Solution {
public:
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
vector<vector<int>> res;
vector<int> tmp;
//int sum = 0;
func(target, tmp, res, candidates);
return res;
}
void func(int target, vector<int>& tmp, vector<vector<int>>& res, vector<int>& candidates) {
if (0 == target) {
vector<int> tmp2 =tmp;
sort(tmp2.begin(), tmp2.end());
if (find(res.begin(), res.end(), tmp2) == res.end()) res.push_back(tmp2);
else return;
}
for (int i = 0; i < candidates.size(); i++) {
if (target >= candidates[i]) {
tmp.push_back(candidates[i]);
func(target - candidates[i], tmp, res, candidates);
tmp.pop_back();
}
}
}
};

more faster

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Solution {
public:
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
vector<vector<int>> res;
vector<int> tmp;
//sort(candidates.begin(), candidates.end());
func(target, tmp, res, candidates, 0);
return res;
}
void func(int target, vector<int>& tmp, vector<vector<int>>& res, vector<int>& candidates, int idx) {
if (0 == target) {
res.push_back(tmp);
return;
}
for (int i = idx; i < candidates.size(); i++) {
//if(i>idx && candidates[i]==candidates[i-1]) continue;
if (target >= candidates[i]) {
tmp.push_back(candidates[i]);
func(target - candidates[i], tmp, res, candidates, i);
tmp.pop_back();
}
}
}
};


90. Subsets II

Description:

Given a collection of integers that might contain duplicates, nums, return all possible subsets.

Note: The solution set must not contain duplicate subsets.

For example,
If nums = [1,2,2], a solution is:

1
2
3
4
5
6
7
8
[
[2],
[1],
[1,2,2],
[2,2],
[1,2],
[]
]

解题思路:
此题与Subsets非常思路相同,只是在加入过程中多用了一个查找来判断重复,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Solution {
public:
vector<vector<int>> subsetsWithDup(vector<int>& nums) {
vector<vector<int>> res;
vector<int> tmp = {};
sort(nums.begin(), nums.end());
int size = nums.size();
func(0, tmp, res, nums);
return res;
}
void func(int idx, vector<int>& tmp, vector<vector<int>>& res, vector<int>& nums) {
if (find(res.begin(), res.end(), tmp) == res.end()) res.push_back(tmp);
else return;
for (int i = idx; i <nums.size(); i++) {
tmp.push_back(nums[i]);
func(i + 1, tmp, res, nums);
tmp.pop_back();
}
}
};


17. Letter Combinations of a Phone Number

Description:

Given a digit string, return all possible letter combinations that the number could represent.

A mapping of digit to letters (just like on the telephone buttons) is given below.
image

1
2
Input:Digit string "23"
Output: ["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].

解题思路:
把这个问题构造成一个普通的回溯问题,差别在于一般回溯问题都是在一个向量上操作的,这个是在二维向量上操作,难点在于把二维向量构造出来,然后就可以了,不会存在重复和排序问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class Solution {
public:
vector<vector<char>> makeTable() { //如果不看这个函数,代码还是挺简洁的 (=´ω`=)
vector<vector<char>> m;
vector<char> tmp0 = {'a', 'b', 'c'};
m.push_back(tmp0);
vector<char> tmp1 = {'d', 'e', 'f'};
m.push_back(tmp1);
vector<char> tmp2 = {'g', 'h', 'i'};
m.push_back(tmp2);
vector<char> tmp3 = {'j', 'k', 'l'};
m.push_back(tmp3);
vector<char> tmp4 = {'m', 'n', 'o'};
m.push_back(tmp4);
vector<char> tmp5 = {'p', 'q', 'r', 's'};
m.push_back(tmp5);
vector<char> tmp6 = {'t', 'u', 'v'};
m.push_back(tmp6);
vector<char> tmp7 = {'w', 'x', 'y', 'z'};
m.push_back(tmp7);
return m;
}
vector<string> letterCombinations(string digits) {
if (digits.empty()) return {};
vector<vector<char>> m = makeTable();
vector<string> res;
string tmp = "";
func(tmp, 0, digits, m, res);
return res;
}
void func(string tmp, int idx, string digits, vector<vector<char>>& m, vector<string>& res) {
if (idx == digits.size()) res.push_back(tmp);
int digits_i = digits[idx] - '2';
for (int i = 0; i < m[digits_i].size(); i++) {
tmp += m[digits_i][i];
func(tmp, idx + 1, digits, m, res);
tmp.pop_back();
}
}
};

40. Combination Sum II

Description:

Given a collection of candidate numbers (C) and a target number (T), find all unique combinations in C where the candidate numbers sums to T.

Each number in C may only be used once in the combination.

Note:
All numbers (including target) will be positive integers.
The solution set must not contain duplicate combinations.
For example, given candidate set [10, 1, 2, 7, 6, 1, 5] and target 8,
A solution set is:

1
2
3
4
5
6
[
[1, 7],
[1, 2, 5],
[2, 6],
[1, 1, 6]
]

解题思路:
这题是在Combination Sum基础上加了两个变化,一个是无序,另一个是重复数字,对应的策略也就是在Combination Sum中被我注释的两行(一开始是抄了人家的代码,没抄明白,发现注释了也能跑通,现在才大致知道作用是什么了)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Solution {
public:
vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
vector<vector<int>> res;
vector<int> tmp;
sort(candidates.begin(), candidates.end());
func(0, target, tmp, candidates, res);
return res;
}
void func(int idx, int target, vector<int>& tmp, vector<int>& candidates, vector<vector<int>>& res) {
//if (target < 0) return;
if (target == 0) res.push_back(tmp);
for(int i = idx; i < candidates.size(); i++) {
if (i > idx && candidates[i] == candidates[i - 1]) continue; //这句的作用在于,一个是让在这一层中,相同的数字不再继续往下找;第二,i > idx 而不是 i > 0是确保了从idx开始的数能够被保存进来
if (target >= candidates[i]) {
tmp.push_back(candidates[i]);
func(i + 1, target - candidates[i], tmp, candidates, res);
tmp.pop_back();
}
else return; //这句让效率从24%到85%,之所以能用,是因为candidates数组已经排序过,数组后面的数必然都不满足
}
}
};

131. Palindrome Partitioning

Description:

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

1
2
3
4
[
["aa","b"],
["a","a","b"]
]

解题思路:
思路很混乱, 第一,先有一个判断回文的函数;然后从前往后逐一取子集,如果当前字符是回文的则保存,并把后续的子字符串当做新字符串,依次迭代。有个难点是,边界问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Solution {
public:
bool ispalindrome(string str) {
for (int i = 0, j = str.size() - 1; i < j; i ++, j--) {
if (str[i] != str[j]) return false;
}
return true;
}
vector<vector<string>> partition(string s) {
vector<vector<string>> res;
vector<string> out;
// string tmp;
func("", s, out, res);
return res;
}
void func(string tmp, string str, vector<string>& out, vector<vector<string>>& res) {
if (str.size() == 1) {
out.push_back(str);
res.push_back(out);
out.pop_back();
return;
}
for (int i = 0; i < str.size(); i++) {
tmp += str[i];
if (ispalindrome(tmp)) {
out.push_back(tmp);
if (i == str.size() - 1) res.push_back(out);
else func("", str.substr(i + 1, str.size() - i - 1), out, res);
out.pop_back();
}
}
}
};

3ms大神的解法(我的是9ms),代码非常简洁易懂,就是学不来。跟我的区别最大的是,他保存的是子串,往下传递的是坐标,方法上比较接近于普通的回溯。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class Solution {
public:
vector<vector<string>> partition(string s) {
vector<vector<string>> ret;
vector<string> tmp;
DFS(ret, tmp, s, 0);
return ret;
}
private:
void DFS(vector<vector<string>>& ret, vector<string>& tmp, string& s, int pos)
{
if (pos == s.size())
{
ret.push_back(tmp);
return;
}
for (int i = pos; i < s.size(); ++i)
{
if(test(s, pos, i))
{
tmp.push_back(s.substr(pos, i-pos+1));
DFS(ret, tmp, s, i+1);
tmp.pop_back();
}
}
}
bool test(string& str, int start, int end) //检查函数
{
while (start < end && str[start] == str[end])
{
++start;
--end;
}
return (start >= end);
}
};


47. Permutations II

Description:

Given a collection of numbers that might contain duplicates, return all possible unique permutations.

For example,
[1,1,2] have the following unique permutations:

1
2
3
4
5
[
[1,1,2],
[1,2,1],
[2,1,1]
]

解题思路:这题在没有1,2两句的情况下,一直受到时间限制的困扰,加上之后就能通过。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Solution {
public:
vector<vector<int>> permuteUnique(vector<int>& nums) {
//vector<int> used(nums.size(), 0);
int size = nums.size();
sort(nums.begin(), nums.end()); //1
vector<vector<int>> res;
vector<int> tmp;
func(tmp, res, size, nums);
return res;
}
void func(vector<int>& tmp, vector<vector<int>>& res, int size, vector<int>& nums) {
if (tmp.size() == size) {
if (find(res.begin(), res.end(), tmp) == res.end()) res.push_back(tmp);
else return;
}
for (auto iter = nums.begin(); iter != nums.end(); iter++) {
//if (used[i] == 0) {
//used[i] = 1;
if (iter > nums.begin() && *iter == *(iter - 1)) continue; //2 在排序之后相等的数字连续出现,重复数字不再进入下层递归,减小了时间复杂度。
int val = *iter;
tmp.push_back(val);
nums.erase(iter);
func(tmp, res, size, nums);
nums.insert(iter, val);
tmp.pop_back();
//used[i] = 0;
//}
}
}
};

目前LeetCode上最快的解,学习无能Orz

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Solution {
public:
void myPermute(vector<int> nums, int start, vector<vector <int>>& result) {
int length = nums.size();
if (start == length) {
result.push_back(nums);
// cout << per[1] << per[2] << per[3] << endl;
return;
}
for (int i = start; i < length; i++) {
if (i != start && nums[i] == nums[start])
continue;
swap(nums[i], nums[start]);
myPermute(nums, start + 1, result);
// swap(nums[i], nums[start]);
}
}
vector<vector<int>> permuteUnique(vector<int>& nums) {
sort(nums.begin(), nums.end());
int length = nums.size();
vector<vector <int>> result;
myPermute(nums, 0, result);
return result;
}
};


60. Permutation Sequence

Description:

The set [1,2,3,…,n] contains a total of n! unique permutations.

By listing and labeling all of the permutations in order,
We get the following sequence (ie, for n = 3):

1
2
3
4
5
6
"123"
"132"
"213"
"231"
"312"
"321"

Given n and k, return the kth permutation sequence.

Note: Given n will be between 1 and 9 inclusive.

解题思路:
如果用Permutations的思路来做的话,会超时。可以发现排序的数字仅看第一位,总是按照,这样的顺序排列的,然后看第一位之后的排序方式有,也就是以1打头的数字排在之间,以2打头的数字排在,以打头的数字排在之间。确定第一个数字之后,后面的序列排在第个,往下依次类推。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class Solution {
public:
string getPermutation(int n, int k) {
string res = "";
vector<int> nums;
for (int i = 1; i <= n; i++) nums.push_back(i);
int a = cntFunc(n - 1);
func(res, k, n, a, nums);
return res;
}
int cntFunc(int n) {
if (n <= 0) return 1;
int res = 1;
for (int i = 1; i <= n; i ++) res *= i;
return res;
}
void func(string& res, int k, int n, int a, vector<int>& nums) {
if (n == 0) return;
//int a = cntFunc(n - 1);
int i = 0;
for (auto iter = nums.begin(); iter != nums.end(); iter ++, i++) {
if ((i * a < k) && ((i + 1) * a >= k)) { //通过条件来剪枝,否则会超时
int val = *iter;
res += val + '0';
if (n > 1) {
nums.erase(iter); //这种操作在VS2015上会报错
func(res, k - (i * a), n - 1, a / (n - 1), nums);
nums.insert(iter, val);
}
else break;
}
}
}
};


93. Restore IP Addresses

Description:

Given a string containing only digits, restore it by returning all possible valid IP address combinations.

For example:
Given "25525511135",

return ["255.255.11.135", "255.255.111.35"]. (Order does not matter)

解题思路:
用一个函数判断字符串是否合法,然后就是回溯

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class Solution {
public:
vector<string> restoreIpAddresses(string s) {
if(s.size() > 4 * 3 || s.size() < 4) return {}; //减少无效字符输入
vector<string> res;
func(0, 4, "", res, s);
return res;
}
void func(int start, int part, string out, vector<string>& res, string s) {
if (start == s.size() && part == 0) {
out.pop_back();
res.push_back(out);
}
for (int i = start, j = 1; i < s.size(); i++, j++) {
if (j > 3) return;
string tmp = s.substr(start, j); //一开始substr用法出错
if (isValid(tmp)) {
tmp += '.';
int len = out.size();
out += tmp;
func(i + 1, part - 1, out, res, s);
out = out.substr(0, len);
}
}
}
bool isValid(string str) {
if (str.empty() || str.size() > 3 || (str.size() > 1 && str[0] == '0')) return false;
else {
int res = 0;
for (int i = 0; i < str.size(); i ++) {
res = res * 10 + str[i] - '0'; // 字符串转数字出错
}
return (res <= 255 && res >=0);
}
}
};

稍微快一档的做法,从6ms到3ms

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class Solution {
public:
vector<string> restoreIpAddresses(string s) {
if(s.size() > 4 * 3 || s.size() < 4) return {};
vector<string> res;
func(0, 4, "", res, s);
return res;
}
void func(int start, int part, string out, vector<string>& res, string s) {
if (start == s.size() && part == 0) {
out.pop_back();
res.push_back(out);
}
if (part <= 0) return; //分出第五段的时候就返回
for (int i = start, j = 1; i < s.size() && j <= 3; i++, j++) {
string tmp = s.substr(start, j);
if (isValid(tmp)) func(i + 1, part - 1, out + tmp + '.', res, s); //减少了一些操作,让加减操作在赋值过程中完成
}
}
bool isValid(string str) {
if (str.empty() || str.size() > 3 || (str.size() > 1 && str[0] == '0')) return false;
else {
int res = 0;
for (int i = 0; i < str.size(); i ++) {
res = res * 10 + str[i] - '0';
}
return (res <= 255 && res >=0);
}
}
};

LeetCode上大神的做法,还没看懂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
class Solution {
public:
vector<string> restoreIpAddresses(string s) {
int minLen = 4;
int maxLen = 12;
vector<string> result;
if (s.size() < minLen || s.size() > maxLen) {
return result;
}
restoreIpAddrHelper(s, "", result, 0);
return result;
}
private:
void restoreIpAddrHelper(string s, string ans,
vector<string> &result, int octNum) {
if (s.size() == 0 && octNum == 4) {
ans.pop_back();
result.push_back(ans);
}
if ( (s.size() == 0 && octNum != 4) ||
(s.size() > 0 && octNum == 4) ){
return;
}
int count = (s.size() > 3) ? 3 : s.size();
for (int i = 1; i <= count; i++) {
string temp = s.substr(0, i);
int number = atoi(temp.c_str());
if (number > 255 || number < 0) {
return;
} else if (temp.size() > 1 && temp[0] == '0') {
return;
}
string ans_temp = ans;
ans_temp.append(temp);
ans_temp.append(".");
restoreIpAddrHelper(s.substr(i, s.size()), ans_temp, result, octNum+1);
}
}
};


Description:
Given a 2D board and a word, find if the word exists in the grid.

The word can be constructed from letters of sequentially adjacent cell, where “adjacent” cells are those horizontally or vertically neighboring. The same letter cell may not be used more than once.

For example,
Given board =

1
2
3
4
5
[
['A','B','C','E'],
['S','F','C','S'],
['A','D','E','E']
]

word = "ABCCED", -> returns true,
word = "SEE", -> returns true,
word = "ABCB", -> returns false.

解题思路:
貌似把题目理解错了,做不粗来~~~~(>_<)~~~~
— 更新:题目没有理解错,但是有不少bug

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
class Solution {
public:
bool exist(vector<vector<char>>& board, string word) {
if (board.empty() || board[0].empty()) return false;
int n = board.size();
int m = board[0].size();
vector<vector<int>> first;
for (int i = 0; i < n; i ++) {
for (int j = 0; j < m; j++) {
if (board[i][j] == word[0]) first.push_back({j, i});//{j, i}应该为{i, j}
}
}
if (first.empty()) return false;
for (auto ch: first) {
bool flag = false;
vector<int> used(n * m, 0);
search(ch[0], ch[1], n, m, 0, word, flag, used, board);
if (flag) return true;
}
return false;
}
//此处坐标反了,和递归函数的赋值不对应
void search(int pos_x, int pos_y, int n, int m, int k, string word, bool& flag, vector<int>& used, vector<vector<char>>& board) {//此处坐标反了,和递归函数的赋值不对应
if (k == word.size()) {flag = true; return;}
char c = word[k];
//找了很久才发现的,board[pos_x][pos_y],坐标反了,应该是board[pos_y][pos_x]
if (pos_x < m && pos_y < n && pos_x >= 0 && pos_y >= 0 && board[pos_x][pos_y] == c) {
//up
if (used[(pos_y - 1) * m + pos_x] == 0) {
used[(pos_y - 1) * m + pos_x] = 1;
search(pos_y - 1, pos_x, n, m, k + 1, word, flag, used, board);
used[(pos_y - 1) * m + pos_x] = 0;
}
//down
if (used[(pos_y + 1) * m + pos_x] == 0) {
used[(pos_y + 1) * m + pos_x] = 1;
search(pos_y + 1, pos_x, n, m, k + 1, word, flag, used, board);
used[(pos_y + 1) * m + pos_x] = 0;
}
//left
if (used[pos_y * m + (pos_x - 1)] == 0) {
used[pos_y * m + (pos_x - 1)] = 1;
search(pos_y, pos_x - 1, n, m, k + 1, word, flag, used, board);
used[pos_y * m + (pos_x - 1)] = 0;
}
//right
if (used[pos_y * m + (pos_x + 1)] == 0) {
used[pos_y * m + (pos_x + 1)] = 1;
search(pos_y, pos_x + 1, n, m, k + 1, word, flag, used, board);
used[pos_y * m + (pos_x + 1)] = 0;
}
}
}
};

修改之后,能运行但超时的版本,后面的版本没有超时的原因是,通过与(||)操作的短路原理来实现剪枝,一旦发现一个true则不再执行后续的内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class Solution {
public:
bool exist(vector<vector<char>>& board, string word) {
if (word.empty()) return true;
if (board.empty() || board[0].empty()) return false;
int n = board.size();
int m = board[0].size();
//vector<vector<int>> first;
vector<bool> used(n * m, false);
for (int i = 0; i < n; i ++) {
for (int j = 0; j < m; j++) {
bool flag = false;
search(i, j, n, m, 0, word, flag, used, board);
}
}
return false;
}
void search(int pos_y, int pos_x, int n, int m, int k, string word, bool& flag, vector<bool>& used, vector<vector<char>>& board) {
if (k == word.size()) {flag = true; return;}
if (pos_x < m && pos_y < n && pos_x >= 0 && pos_y >= 0 && board[pos_y][pos_x] == word[k]) {
if (used[pos_y * m + pos_x] == true) return;
used[pos_y * m + pos_x] = true;
//up
search(pos_y - 1, pos_x, n, m, k + 1, word, flag, used, board);
//down
search(pos_y + 1, pos_x, n, m, k + 1, word, flag, used, board);
//left
search(pos_y, pos_x - 1, n, m, k + 1, word, flag, used, board);
//right
search(pos_y, pos_x + 1, n, m, k + 1, word, flag, used, board);
used[pos_y * m + pos_x] = false;
}
else return;
}
};

加入操作,不能通过版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class Solution {
public:
bool exist(vector<vector<char>>& board, string word) {
if (word.empty()) return true;
if (board.empty() || board[0].empty()) return false;
int n = board.size();
int m = board[0].size();
vector<bool> used(n * m, false);
for (int i = 0; i < n; i ++) {
for (int j = 0; j < m; j++) {
if (search(i, j, n, m, 0, word, used, board)) return true;
}
}
return false;
}
bool search(int pos_y, int pos_x, int n, int m, int k, string word, vector<bool>& used, vector<vector<char>>& board) {
if (k == word.size()) return true;
//问题就在board[pos_x][pos_y]坐标反了,半天没查出来
if (pos_x >= m || pos_y >= n || pos_x < 0 || pos_y < 0 || used[pos_y * m + pos_x] == true ||board[pos_x][pos_y] != word[k]) return false;
used[pos_y * m + pos_x] = true;
bool res = search(pos_y - 1, pos_x, n, m, k + 1, word, used, board) //up
|| search(pos_y + 1, pos_x, n, m, k + 1, word, used, board) //down
|| search(pos_y, pos_x - 1, n, m, k + 1, word, used, board) //left
|| search(pos_y, pos_x + 1, n, m, k + 1, word, used, board); //right
used[pos_y * m + pos_x] = false;
return res;
}
};

调了半天后的AC版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Solution {
public:
bool exist(vector<vector<char>>& board, string word) {
if (word.empty()) return true;
if (board.empty() || board[0].empty()) return false;
int n = board.size(); int m = board[0].size();
vector<bool> used(n * m, false);
for (int i = 0; i < n; i ++) {
for (int j = 0; j < m; j++) {
if (search(i, j, 0, n, m, word, used, board)) return true;
}
}
return false;
}
bool search(int pos_y, int pos_x, int k, int n, int m,string word, vector<bool>& used, vector<vector<char>>& board) {
if (k == word.size()) return true;
if (pos_x >= m || pos_y >= n || pos_x < 0 || pos_y < 0 || used[pos_y * m + pos_x] == true || board[pos_y][ pos_x] != word[k]) return false;
used[pos_y * m + pos_x] = true;
bool res = search(pos_y - 1, pos_x, k + 1, n, m, word, used, board) //up
|| search(pos_y + 1, pos_x, k + 1, n, m, word, used, board) //down
|| search(pos_y, pos_x - 1, k + 1, n, m, word, used, board) //left
|| search(pos_y, pos_x + 1, k + 1, n, m, word, used, board); //right
used[pos_y * m + pos_x] = false;
return res;
}
};


211. Add and Search Word - Data structure design

Description

Design a data structure that supports the following two operations:

1
2
void addWord(word)
bool search(word)

search(word) can search a literal word or a regular expression string containing only letters a-z or .. A . means it can represent any one letter.

For example:

1
2
3
4
5
6
7
addWord("bad")
addWord("dad")
addWord("mad")
search("pad") -> false
search("bad") -> true
search(".ad") -> true
search("b..") -> true

Note:
You may assume that all words are consist of lowercase letters a-z.

Hint:
You should be familiar with how a Trie works. If not, please work on this problem: Implement Trie (Prefix Tree) first.
解题思路:
不愧是boss关,不会做。
建立字典树,然后通过字典树查找

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
class WordDictionary {
public:
/** Initialize your data structure here. */
struct TrieNode {
public:
TrieNode *child[26];
bool isWord;
TrieNode() : isWord(false) {
for (auto &a: child) { a = NULL;}
}
};
WordDictionary() {
root = new TrieNode();
}
/** Adds a word into the data structure. */
void addWord(string word) {
TrieNode *p = root;
for (auto &a: word) {
int i = a - 'a';
if (!p -> child[i]) p -> child[i] = new TrieNode();
p = p -> child[i];
}
p -> isWord = true;
}
/** Returns if the word is in the data structure. A word could contain the dot character '.' to represent any one letter. */
bool search(string word) {
return searchWord(word, root, 0);
}
bool searchWord(string& word, TrieNode *p, int i) {
if (i == word.size()) return p -> isWord;
if (word[i] == '.') {
for (auto &a: p -> child) {
if (a && searchWord(word, a, i + 1)) return true;
}
return false;
}
else {
return p -> child[word[i] - 'a'] && searchWord(word, p -> child[word[i] - 'a'], i + 1);
}
}
private:
TrieNode *root;
};


Hard

52. N-Queens II

Description:
Follow up for N-Queens problem.

Now, instead outputting board configurations, return the total number of distinct solutions.

通过的Hard难度的第一题

解题思路:
深度优先方法,剔除掉对角线(其实是左上和右上)以及上方可能出现的棋子的情况进行递归和返回。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class Solution {
public:
int totalNQueens(int n) {
vector<bool> used(n * n, false);
return func(0, n, used);
}
int func(int i, int n, vector<bool>& used) {
int cnt = 0;
if (i == n) return 1;
for (int j = 0; j < n; j ++) {
bool f = true;
for (int ii = 0; ii < i; ii ++) {
if (used[ii *n + j]) {
f = false; break;
}
}
if (f == false) continue; //加上之后,略快一点儿
int min = i < j ? i : j;
for (int a = 1; a <= min; a++) {
if (used[(i - a) * n + j - a]) {
f = false; break;
}
}
if (f == false) continue;
min = i < (n - j - 1) ? i : (n - j - 1);
for (int a = 1; a <= min; a++) {
if (used[(i -a) * n + j + a]) {
f = false; break;
}
}
if (f) {
used[i * n + j] = true;
cnt += func(i + 1, n , used);
used[i * n + j] = false;
}
}
return cnt;
}
};

51. N-Queens

Description:
The n-queens puzzle is the problem of placing n queens on an n×n chessboard such that no two queens attack each other.

tu

Given an integer n, return all distinct solutions to the n-queens puzzle.

Each solution contains a distinct board configuration of the n-queens’ placement, where 'Q' and '.' both indicate a queen and an empty space respectively.

For example,
There exist two distinct solutions to the 4-queens puzzle:

1
2
3
4
5
6
7
8
9
10
11
[
[".Q..", // Solution 1
"...Q",
"Q...",
"..Q."],
["..Q.", // Solution 2
"Q...",
"...Q",
".Q.."]
]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
class Solution {
public:
vector<vector<string>> solveNQueens(int n) {
vector<vector<string>> res;
vector<string> table;
for (int i = 0; i < n; i++) {
string tmp = "";
for (int j = 0; j < n; j ++) {
tmp += ((i ==j) ? 'Q' : '.');
}
table.push_back(tmp);
}
vector<string> tmp;
vector<bool> used(n * n, false);
func(0, n, used, table, tmp, res);
return res;
}
void func(int i, int n, vector<bool>& used, vector<string>& table, vector<string>& tmp, vector<vector<string>>& res) {
int cnt = 0;
if (i == n) res.push_back(tmp);
for (int j = 0; j < n; j ++) {
bool f = true;
for (int ii = 0; ii < i; ii ++) {
if (used[ii *n + j]) {
f = false; break;
}
}
if (f == false) continue;
int min = i < j ? i : j;
for (int a = 1; a <= min; a++) {
if (used[(i - a) * n + j - a]) {
f = false; break;
}
}
if (f == false) continue;
min = i < (n - j - 1) ? i : (n - j - 1);
for (int a = 1; a <= min; a++) {
if (used[(i -a) * n + j + a]) {
f = false; break;
}
}
if (f) {
used[i * n + j] = true;
tmp.push_back(table[j]);
func(i + 1, n , used, table, tmp, res);
tmp.pop_back();
used[i * n + j] = false;
}
}
}
};

ci chu you tu

Other

Works Application笔试题 Coin Game

Description:
There is a rectangular chessboard containing N*M cells, each of which either has one coin or nothing.

You can move all the coins together in one direction (such as up, down, left, and right), but each time you can move these coins by only one cell.

If any coins fall out of the chessboard, they must be thrown away.

If it is required to keep K coins on the board, what is the minimum moves you have to take?

Output -1 if you can not meet this requirement.

输入描述:
The first line of the input are two positive integers n, representing the size of the board. For the next n line(s), each line has m numbers of characters, with ‘o’ indicating a coin, ‘.’ indicates an empty grid. The last line is a positive integer k, indicating the number of coins to be retained.

30% small input:
40% medium input:
30% large input:

输出描述:

1
2
Output
an integer that represents the number of moves, no solution output -1.

示例1
输入

1
2
3
4
5
3 4
.o..
oooo
..o.
3

输出

1
2

50% case通过率,复杂度过大,回溯如何剪枝???

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int countFunc(int start_row, int end_row, int start_col, int end_col, vector<vector<char>> input) {
int cnt = 0;
for (int i = start_row; i < end_row; i++) {
for (int j = start_col; j < end_col; j++) {
if (input[i][j] == 'o') cnt ++;
}
}
return cnt;
}
void func(int k, vector<vector<char>>& input, int start_row, int end_row, int start_col, int end_col, int count, vector<int>& res) {
int tmp_cnt = countFunc(start_row, end_row, start_col, end_col, input);
if (tmp_cnt == k) {
res.push_back(count);
return;
}
else if (tmp_cnt < k) return;
// up
if (start_row < end_row) {
start_row += 1;
count += 1;
func(k, input, start_row, end_row, start_col, end_col, count, res);
count -= 1;
start_row -= 1;
}
// down
if (start_row < end_row) {
end_row -= 1;
count += 1;
func(k, input, start_row, end_row, start_col, end_col, count, res);
count -= 1;
end_row += 1;
}
// left
if (start_col < end_col) {
start_col += 1;
count += 1;
func(k, input, start_row, end_row, start_col, end_col, count, res);
count -= 1;
start_col -= 1;
}
// right
if (start_col < end_col) {
end_col -= 1;
count += 1;
func(k, input, start_row, end_row, start_col, end_col, count, res);
count -= 1;
end_col += 1;
}
}
int main() {
int n, m, k;
char tmp;
cin >> n;
cin >> m;
vector<char> temp;
vector<vector<char>> input;
int i = 1;
while (cin >> tmp) {
temp.push_back(tmp);
if (i % m == 0) {
input.push_back(temp);
temp = {};
}
i++;
if (i == m * n + 1) {
cin >> k;
}
}
vector<int> res;
int start_row = 0;
int end_row = input.size();
int start_col = 0;
int end_col = input.size();
func(k, input, start_row, end_row, start_col, end_col, 0, res);
if (res.empty()) cout << -1 << endl;
else {
sort(res.begin(), res.end());
cout << res[0] << endl;
}
return 0;
}