一、Gray Code

  1. class Solution
  2. {
  3. public:
  4. vector<int> grayCode(int n)
  5. {
  6. vector<int> result={};
  7. if(n==) return result;
  8. return dfs(n);
  9. }
  10. vector<int> dfs(int n)
  11. {
  12. if(n==)
  13. {
  14. vector<int> v={,};
  15. return v;
  16. }
  17. vector<int> tmp=dfs(n-);
  18. int len=tmp.size();
  19. for(int i=len-;i>=;i--)
  20. {
  21. tmp.push_back(tmp[i]+len);
  22. }
  23. return tmp;
  24. }
  25. };

这个方法主要是利用对称性。但是性能上并不是太好。最优解法应该是利用位运算。参考此代码中的第一个评论

需要注意,在n=0时返回的不是空vector,而是{0}.

二、Generate Parentheses

update 8.13

  1. class Solution {
  2. public:
  3. vector<string> generateParenthesis(int n) {
  4. vector<string> res;
  5. string tmp;
  6. string brackets("()");
  7. dfs(res, tmp, n, , , brackets);
  8. return res;
  9. }
  10.  
  11. void dfs(vector<string>& res, string tmp, int n, int left, int right, string brackets) {
  12. if (left == n && right == n) {
  13. res.push_back(tmp);
  14. return;
  15. }
  16. for (int i = ; i < brackets.size(); i++) {
  17. if ((i == && left <= right) || (left == n && i == )) {
  18. continue;
  19. }
  20. tmp += brackets[i];
  21. if (i == ) {
  22. dfs(res, tmp, n, left + , right, brackets);
  23. } else {
  24. dfs(res, tmp, n, left, right + , brackets);
  25. }
  26. tmp.pop_back();
  27. }
  28. }
  29. };

按照之前的套路来写的。就是对于dfs的for循环而言,应当是对于同一个位置的所有可能情况来循环的。

写的有点繁琐。

  1. class Solution
  2. {
  3. public:
  4. vector<string> generateParenthesis(int n)
  5. {
  6. if(n==) return result;
  7. finalPos=*n;
  8. left=n;
  9. right=n;
  10. dfs();
  11. return result;
  12. }
  13. void dfs(int n)
  14. {
  15. if(n==finalPos)
  16. {
  17. result.push_back(tmp);
  18. return;
  19. }
  20. if(dif> && right>)
  21. {
  22. right--;
  23. dif--;
  24. tmp+=')';
  25. dfs(n+);
  26. tmp.resize(tmp.size()-);
  27. right++;
  28. dif++;
  29. }
  30. if(left>)
  31. {
  32. left--;
  33. dif++;
  34. tmp+='(';
  35. dfs(n+);
  36. tmp.resize(tmp.size()-);
  37. left++;
  38. dif--;
  39. }
  40. }
  41. int finalPos;
  42. int dif=;
  43. string tmp;
  44. int left;
  45. int right;
  46. vector<string> result;
  47. };

下面的代码参考于此。非常巧妙,简洁。

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

三、Permutation

1. Permutations

  1. class Solution
  2. {
  3. public:
  4. vector<vector<int>> permute(vector<int> &num)
  5. {
  6. dfs(num);
  7. return result;
  8. }
  9. private:
  10. vector<vector<int>> result;
  11. vector<int> path;
  12. void dfs(vector<int> &num)
  13. {
  14. if(path.size()==num.size())
  15. {
  16. result.push_back(path);
  17. return;
  18. }
  19. for(int i=;i<num.size();i++)
  20. {
  21. if(find(path.begin(),path.end(),num[i])==path.end())
  22. {
  23. path.push_back(num[i]);
  24. dfs(num);
  25. path.pop_back();
  26. }
  27. }
  28. }
  29. };

refer to soulMach.中规中矩,可以一用。

  1. class Solution
  2. {
  3. public:
  4. vector<vector<int>> permute(vector<int> &nums)
  5. {
  6. len=nums.size();
  7. vector<vector<int>> result;
  8. for(int i=;i<nums.size();i++)
  9. umap[nums[i]]=;
  10. helper(result,umap,);
  11. return result;
  12. }
  13. void helper(vector<vector<int>> &result,unordered_map<int,int> &umap,int n)
  14. {
  15. if(n==len)
  16. {
  17. result.push_back(tmp);
  18. return;
  19. }
  20. for(auto it=umap.begin();it!=umap.end();it++)
  21. {
  22. if(it->second==)
  23. {
  24. tmp.push_back(it->first);
  25. umap[it->first]=;
  26. helper(result,umap,n+);
  27. umap[it->first]=;
  28. tmp.resize(tmp.size()-);
  29. }
  30. }
  31. }
  32. int len;
  33. unordered_map<int,int> umap;
  34. vector<int> tmp;
  35. };

中规中矩的做法。不过多分配了一个umap.

较简洁的做法

  1. class Solution {
  2. public:
  3. vector<vector<int> > permute(vector<int> &num) {
  4. vector<vector<int> > result;
  5. permuteRecursive(num, , result);
  6. return result;
  7. }
  8. void permuteRecursive(vector<int> &num, int begin, vector<vector<int> > &result) {
  9. if (begin >= num.size()) {
  10. result.push_back(num);
  11. return;
  12. }
  13. for (int i = begin; i < num.size(); i++) {
  14. swap(num[begin], num[i]);
  15. permuteRecursive(num, begin + , result);
  16. swap(num[begin], num[i]);
  17. }
  18. }
  19. };

2. Permutations II

  1. class Solution
  2. {
  3. public:
  4. vector<vector<int>> permuteUnique(vector<int> &nums)
  5. {
  6. for(auto n:nums)
  7. umap[n]++;
  8. dfs(nums);
  9. return result;
  10. }
  11. private:
  12. vector<vector<int>> result;
  13. vector<int> path;
  14. unordered_map<int,int> umap;
  15. void dfs(vector<int> &nums)
  16. {
  17. if(path.size()==nums.size())
  18. {
  19. result.push_back(path);
  20. return;
  21. }
  22. for(auto p:umap)
  23. {
  24. if(p.second>)
  25. {
  26. path.push_back(p.first);
  27. umap[p.first]--;//不能用p.second--.因为p是临时变量,影响不到umap。
  28. dfs(nums);
  29. umap[p.first]++;
  30. path.pop_back();
  31. }
  32. }
  33. }
  34. };

注意:老是会忘刚开始时初始化umap!

比较奇怪的是,在dfs函数里,如果把对umap遍历改为对vector num遍历,就会TLE。

这里还遇到一个问题,就是我一开始遍历umap时用的是for(auto p:umap),然后对p.second做修改后是影响不到umap[p.first]的。因为这里的p相当于一个临时变量,无法影响umap的映射关系。如果换成指针的话就可以了。

对比两个permute。

对于不含重复元素的permute,如果以前没用过该元素,就可以用。即需要判定下path中是否含有该元素。若没有,即可用之。

对于含有重复元素的permute,借助于umap存储每个元素的个数。dfs时遍历umap,只要umap中该元素个数大于0,就可以用之。

四、Combination & Subset

1. Combinations

  1. class Solution
  2. {
  3. public:
  4. vector<vector<int>> combine(int n, int k)
  5. {
  6. dfs(n, k, );
  7. return result;
  8. }
  9. private:
  10. vector<vector<int>> result;
  11. vector<int> path;
  12. void dfs(int n, int k, int start)
  13. {
  14. if (path.size() == k)
  15. {
  16. result.push_back(path);
  17. return;
  18. }
  19. for (int i = start; i <= n; i++)
  20. {
  21. // if (find(path.begin(), path.end(), i) == path.end())
  22. path.push_back(i);
  23. dfs(n, k, i+ );//dfs(n, k, start + 1);
  24. path.pop_back();
  25. }
  26. }
  27. };

在combination里,和permutation不同的地方在于,为了保证不会出现重复,遍历的时候按照升序来,即后一个数一定要大于前一个数。

同时,由于保证了后一个数必定比前一个数大,那么其实就保证了唯一性,也就不用find去判断是否在path里出现过了。

另外注意for循环的i是从start开始,而next dfs里的start是取的i+1而不是start+1.

2. Subsets

递归版

[Templated]

  1. class Solution
  2. {
  3. public:
  4. vector<vector<int>> subsets(vector<int> &nums)
  5. {
  6. sort(nums.begin(),nums.end());
  7. dfs(nums,);
  8. return result;
  9. }
  10. vector<vector<int>> result;
  11. vector<int> path;
  12. void dfs(vector<int> &nums,int start)
  13. {
  14. result.push_back(path);
  15. for(int i=start;i<nums.size();i++)
  16. {
  17. path.push_back(nums[i]);
  18. dfs(nums,i+);
  19. path.pop_back();
  20. }
  21. }
  22. };

该模板化版本和combination与subsets II都很类似。它和combination的区别在于,dfs函数一开始不需要判断长度就压入result,因为它不需要在乎长度。

和subsets II的区别在于,不用判重。

版本2:

  1. class Solution
  2. {
  3. public:
  4. vector<vector<int>> subsets(vector<int> &nums)
  5. {
  6. sort(nums.begin(),nums.end());
  7. dfs(nums,);
  8. return result;
  9. }
  10. vector<vector<int>> result;
  11. vector<int> path;
  12. void dfs(vector<int> &nums, int step)
  13. {
  14. if(step==nums.size())
  15. {
  16. result.push_back(path);
  17. return;
  18. }
  19. path.push_back(nums[step]);
  20. dfs(nums,step+);
  21. path.pop_back();
  22. dfs(nums,step+);
  23. }
  24. };

一开始打算利用combinations,即依次指定combination的长度。但是那样很繁琐。

需要注意的是Elements in a subset must be in non-descending order。所以在操作之前需要先sort一下;

迭代版

  1. class Solution
  2. {
  3. public:
  4. vector<vector<int>> subsets(vector<int> &nums)
  5. {
  6. vector<vector<int>> result(,vector<int>());
  7. sort(nums.begin(),nums.end());
  8. for(int i=;i<nums.size();i++)
  9. {
  10. int n=result.size();
  11. for(int j=;j<n;j++)
  12. {
  13. result.push_back(result[j]);
  14. result.back().push_back(nums[i]);
  15. }
  16. }
  17. return result;
  18. }
  19. };

基本思路就是每次循环都把当前vector已有的子集合拷贝一份并在末尾添加一个新元素。再加入到vector里。比如对于{1,2,3},就先加入空,然后复制一下空并末尾添加1(当前子集合即为{空,{1}}),然后把空和{1}再各拷贝一份并在末尾添上2(即{2},{1,2}),并加入原vector。子集合变成了{空,{1},{2},{1,2}},再拷贝一份当前vector所有内容并在各自末尾添上3({3},{1,2},{2,3},{1,2,3}),并加入到vector里,变成(空,{1},{2},{1,2}, {3},{1,2},{2,3},{1,2,3})。

用二进制法也可以让空间复杂度降为O(1),参考soulMach。

3. Subsets II

  1. class Solution
  2. {
  3. public:
  4. vector<vector<int>> subsetsWithDup(vector<int> &nums)
  5. {
  6. sort(nums.begin(),nums.end());
  7. dfs(nums,);
  8. return result;
  9. }
  10. vector<vector<int>> result;
  11. vector<int> path;
  12. void dfs(vector<int> &nums, int start)
  13. {
  14. result.push_back(path);
  15. for(int i=start;i<nums.size();i++)
  16. {
  17. if(i!=start && nums[i]==nums[i-]) continue;
  18. path.push_back(nums[i]);
  19. dfs(nums,i+);
  20. path.pop_back();
  21. }
  22. }
  23. };

在产生path时,path的任何一个确定的位置(如第3个位置,第5个位置),都不选相同值的元素,这样就能防止结果中有重复。

那怎么叫确定的位置呢?在代码Line15的for循环里,遍历的每个元素都处于同一层,它们都是将要放置在或不放置在某一个相同的位置。(这里的位置值得是结果path中的位置,比如{1,2,3}这个path,第2个位置是2,第3个位置是3.) for 循环里的每个元素都是面临对同一个位置做取舍决定。

每嵌套调用一个dfs,就走的更深一层,体现在path的位数增加1. 类似于:

  1. dfs
  2. {//现在path长度为1
  3. dfs
  4. {//现在path长度为2
  5. dfs
  6. {//现在path长度为3
  7. ......
  8. }
  9. //现在path长度又恢复为2.继续遍历for循环的下一元素来填充path
  10. ......
  11. }
  12. //现在path长度又恢复为1.继续遍历for循环的下一元素来填充path
  13. ......
  14. }

回头去看,该算法就是保证了相同深度的path的末尾元素不选取选过的元素。所以可以通过在dfs里加个for循环(并且循环内部加个判断去重)来实现。

refer to this blog.

4. Combination Sum

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

5. Combination Sum II

  1. class Solution
  2. {
  3. public:
  4. vector<vector<int>> combinationSum2(vector<int> &candidates,int target)
  5. {
  6. sort(candidates.begin(),candidates.end());
  7. dfs(candidates,,,target);
  8. return result;
  9. }
  10. vector<vector<int>> result;
  11. vector<int> path;
  12. void dfs(vector<int> &nums, int start,int sum,int target)
  13. {
  14. if(sum>target) return;
  15. if(sum==target)
  16. {
  17. result.push_back(path);
  18. return;
  19. }
  20. for(int i=start;i<nums.size();i++)
  21. {
  22. if(i!=start && nums[i]==nums[i-]) continue;
  23. path.push_back(nums[i]);
  24. dfs(nums,i+,sum+nums[i],target);
  25. path.pop_back();
  26. }
  27. }
  28. };

Combination sum II 比 I 就多在加一个判重。和之前subsets II 比 I 多一句判重是一样的。

6. Combination Sum III

  1. class Solution
  2. {
  3. public:
  4. vector<vector<int>> combinationSum3(int k,int n)
  5. {
  6. vector<int> nums={,,,,,,,,};
  7. dfs(nums,k,n,,);
  8. return result;
  9. }
  10. vector<vector<int>> result;
  11. vector<int> path;
  12. void dfs(vector<int> &nums,int k,int n,int start,int sum)
  13. {
  14. if(sum==n && path.size()==k)
  15. {
  16. result.push_back(path);
  17. return;
  18. }
  19. if(sum>n||path.size()>=k) return;
  20. for(int i=start;i<nums.size();i++)
  21. {
  22. path.push_back(nums[i]);
  23. dfs(nums,k,n,i+,sum+nums[i]);
  24. path.pop_back();
  25. }
  26. }
  27. };

判断的时候多了个限制条件,需要既让sum相等同时也要让path.size()等于k。

小结:对于combination,subset这种题,元素顺序不同不能作为区分点(不像permutation那样)。因此为了防止dfs时会出现{1,2}和{2,1}这种,需要借助start这个机制来确保下一个将要遍历的元素在位置上必须在上一个遍历的元素之后。

五、Palindrome Partitioning

  1. class Solution
  2. {
  3. public:
  4. vector<vector<string>> partition(string s)
  5. {
  6. dfs(,s);
  7. return result;
  8. }
  9. vector<vector<string>> result;
  10. vector<string> path;
  11. void dfs(int start,string &s)
  12. {
  13. if(start==s.length())
  14. {
  15. result.push_back(path);
  16. return;
  17. }
  18. for(int i=start;i<s.length();i++)
  19. {
  20. if(isPalindrome(s.substr(start,i-start+)))
  21. {
  22. path.push_back(s.substr(start,i-start+));
  23. dfs(i+,s);
  24. path.pop_back();
  25. }
  26. }
  27. }
  28. bool isPalindrome(string s)
  29. {
  30. if(s.empty()) return true;
  31. int len=s.length();
  32. for(int i=;i<len/;i++)
  33. {
  34. if(s[i]!=s[len-i-])
  35. return false;
  36. }
  37. return true;
  38. }
  39. };

注意Line34,是s[len-1-i],不是s[len-i].

Palindrome Partitioning有DP解法。后序补充上。

六、Letter Combinations of a Phone Number

  1. class Solution
  2. {
  3. public:
  4. vector<string> letterCombinations(string digits)
  5. {
  6. if(digits.empty()) return result;
  7. initialization();
  8. dfs(digits,);
  9. return result;
  10. }
  11. private:
  12. vector<string> result;
  13. string path;
  14. unordered_map<int,string> umap;
  15. void dfs(string s,int step)
  16. {
  17. if(path.size()==s.size())
  18. {
  19. result.push_back(path);
  20. return;
  21. }
  22. string tmp=umap[int(s[step]-'')];
  23. for(int i=;i<tmp.size();i++)
  24. {
  25. path.push_back(tmp[i]);
  26. dfs(s,step+);
  27. path.pop_back();
  28. }
  29. }
  30. void initialization()
  31. {
  32. umap[]="abc";
  33. umap[]="def";
  34. umap[]="ghi";
  35. umap[]="jkl";
  36. umap[]="mno";
  37. umap[]="pqrs";
  38. umap[]="tuv";
  39. umap[]="wxyz";
  40. umap[]=" ";
  41. }
  42. };

 注意:Line6!这种corner case要格外小心!

七、 Permutation Sequence

  1. class Solution
  2. {
  3. public:
  4. string getPermutation(int n,int k)
  5. {
  6. string result(n,' ');
  7. string s("");
  8. string str=s.substr(,n);
  9. k--;
  10. for(int i=;i<n;i++)
  11. {
  12. int tmp=factorial(str.size()-);
  13. int q=k/tmp;
  14. result[i]=str[q];
  15. str.erase(q,);
  16. k-=tmp*q;
  17. }
  18. return result;
  19. }
  20. int factorial(int n)
  21. {
  22. if(n==) return ;
  23. int result=;
  24. for(int i=;i<=n;i++)
  25. result*=i;
  26. return result;
  27. }
  28. };

主要利用康托展开。康托展开详细内容参见此文

八、Sudoku

1. Sudoku Solver

  1. class Solution
  2. {
  3. public:
  4. void solveSudoku(vector<vector<char>> &board)
  5. {
  6. dfs(board,);
  7. }
  8. bool dfs(vector<vector<char>> &board, int pos)
  9. {
  10. if(pos>=) return true;
  11. int row=pos/;
  12. int col=pos%;
  13. if(board[row][col]!='.')
  14. return dfs(board,pos+);
  15. else
  16. {
  17. for(char c='';c<='';c++)
  18. {
  19. if(check(board,row,col,c))
  20. {
  21. board[row][col]=c;
  22. if(dfs(board,pos+)) return true;
  23. else board[row][col]='.';
  24. }
  25. }
  26. return false;
  27. }
  28. }
  29. bool check(vector<vector<char>> &board, int row,int col,char value)
  30. {
  31. for(int i=;i<;i++)
  32. if(board[row][i]==value) return false;
  33. for(int i=;i<;i++)
  34. if(board[i][col]==value) return false;
  35. for(int i=;i<;i++)
  36. {
  37. int r=row/*+i/;
  38. int c=col/*+i%;
  39. if(board[r][c]==value) return false;
  40. }
  41. return true;
  42. }
  43. };

注意一些技巧。Line11,12通过对board.size取余和取商,将二维的遍历转化为一维的遍历,每次只需pos++即可。

Line37,38也类似,对每个方块遍历时,(row/3*3,col/3*3)能得到坐标为(row,col)的点所在的方块的左上角的点对应的坐标。

2. Valid Sudoku

  1. class Solution {
  2. public:
  3. bool isValidSudoku(vector<vector<char>> &board)
  4. {
  5. vector<bool> mark(,false);
  6. for(int i=;i<;i++)
  7. {
  8. fill(mark.begin(),mark.end(),false);
  9. for(int j=;j<;j++)
  10. {
  11. if(board[i][j]=='.') continue;
  12. if(mark[board[i][j]-'']==true) return false;
  13. mark[board[i][j]-'']=true;
  14. }
  15. }
  16. for(int j=;j<;j++)
  17. {
  18. fill(mark.begin(),mark.end(),false);
  19. for(int i=;i<;i++)
  20. {
  21. if(board[i][j]=='.') continue;
  22. if(mark[board[i][j]-'']==true) return false;
  23. mark[board[i][j]-'']=true;
  24. }
  25. }
  26. for(int i=;i<;i+=)
  27. {
  28. for(int j=;j<;j+=)
  29. {
  30. fill(mark.begin(),mark.end(),false);
  31. for(int k=;k<;k++)
  32. {
  33. int row=i+k/;
  34. int col=j+k%;
  35. if(board[row][col]=='.') continue;
  36. if(mark[board[row][col]-'']==true) return false;
  37. mark[board[row][col]-'']=true;
  38. }
  39. }
  40. }
  41. return true;
  42. }
  43. };

分别从行,列,块的角度来check是否符合valid。(不是DFS类的,只是因为同属于Sudoku问题所以放进来。)

九、Restore IP Addresses

  1. class Solution
  2. {
  3. public:
  4. vector<string> restoreIpAddresses(string s)
  5. {
  6. dfs(s,,);
  7. return result;
  8. }
  9. vector<string> result;
  10. string path;
  11. void dfs(string s,int start,int step)
  12. {
  13. if (step == )
  14. {
  15. if (start == s.size())
  16. result.push_back(path);
  17. return;
  18. }
  19. for (int i = start; i < start + && i<s.size(); i++)
  20. {
  21. string tmp = s.substr(start, i - start + );
  22. if (isValid(tmp))
  23. {
  24. path += tmp;
  25. if (step < ) path += '.';
  26. dfs(s,i+,step+);
  27. path.resize(path.size()-tmp.size());
  28. if (step < ) path.resize(path.size()-);
  29. }
  30. }
  31. }
  32. bool isValid(string s)
  33. {
  34. if (s.size() > && s[] == '') return false;
  35. return (stoi(s) >= && stoi(s) <= );
  36. }
  37. };

第一遍做错了。除了标记start之外,还要记录当前partition几次了。超过4次就得return了。

十、Trie

1. Implement Trie (Prefix Tree)

  1. class TrieNode
  2. {
  3. public:
  4. char content;
  5. bool isend;
  6. int shared;
  7. vector<TrieNode*> children;
  8. TrieNode():content(' '),isend(false),shared(){}
  9. TrieNode(char ch):content(ch),isend(false),shared(){}
  10. TrieNode* subNode(char ch)
  11. {
  12. for(auto child:children)
  13. {
  14. if(child->content==ch)
  15. return child;
  16. }
  17. return NULL;
  18. }
  19. ~TrieNode()
  20. {
  21. for(auto child:children)
  22. delete child;
  23. }
  24. };
  25.  
  26. class Trie
  27. {
  28. private:
  29. TrieNode* root;
  30. public:
  31. Trie()
  32. {
  33. root=new TrieNode();
  34. }
  35. void insert(string s)
  36. {
  37. if(search(s)) return;
  38. TrieNode* curr=root;
  39. for(auto ch:s)
  40. {
  41. TrieNode* child=curr->subNode(ch);
  42. if(child!=NULL)
  43. curr=child;
  44. else
  45. {
  46. TrieNode* newNode=new TrieNode(ch);
  47. curr->children.push_back(newNode);
  48. curr=newNode;
  49. }
  50. ++curr->shared;
  51. }
  52. curr->isend=true;
  53. }
  54. bool search(string key)
  55. {
  56. TrieNode* curr=root;
  57. for(auto ch:key)
  58. {
  59. curr=curr->subNode(ch);
  60. if(curr==NULL) return false;
  61. }
  62. return curr->isend==true;
  63. }
  64. bool startsWith(string prefix)
  65. {
  66. TrieNode* curr=root;
  67. for(auto ch:prefix)
  68. {
  69. curr=curr->subNode(ch);
  70. if(curr==NULL) return false;
  71. }
  72. return true;
  73. }
  74. ~Trie()
  75. {
  76. delete root;
  77. }
  78. };

reference here.

2. Add and Search Word - Data structure design

  1. class TrieNode
  2. {
  3. public:
  4. char content;
  5. vector<TrieNode*> children;
  6. bool isend;
  7. int shared;
  8. TrieNode* subNode(char ch)
  9. {
  10. for(auto child:children)
  11. if(child->content==ch) return child;
  12. return NULL;
  13. }
  14. TrieNode():content(' '),isend(),shared(){}
  15. TrieNode(char ch):content(ch),isend(),shared(){}
  16. ~TrieNode()
  17. {
  18. for(auto child:children)
  19. delete child;
  20. }
  21. };
  22.  
  23. class WordDictionary
  24. {
  25. public:
  26. void addWord(string word)
  27. {
  28. TrieNode* curr=root;
  29. for(auto ch:word)
  30. {
  31. TrieNode* child=curr->subNode(ch);
  32. if(child!=NULL)
  33. curr=child;
  34. else
  35. {
  36. TrieNode* newNode=new TrieNode(ch);
  37. curr->children.push_back(newNode);
  38. curr=newNode;
  39. }
  40. }
  41. curr->isend=true;
  42. }
  43. bool search(string word)
  44. {
  45. return dfs(word,root);
  46. }
  47. bool dfs(string word,TrieNode* root)
  48. {
  49. if(word.empty()) return root->isend;
  50. if(word[]=='.')
  51. {
  52. for(auto child:root->children)
  53. if(dfs(word.substr(,word.size()-),child)==true)
  54. return true;
  55. return false;
  56. }
  57. else
  58. {
  59. TrieNode* curr=root->subNode(word[]);
  60. if(curr==NULL) return false;
  61. else return dfs(word.substr(,word.size()-),curr);
  62. }
  63. }
  64. WordDictionary()
  65. {
  66. root=new TrieNode();
  67. }
  68. ~WordDictionary()
  69. {
  70. delete root;
  71. }
  72. private:
  73. TrieNode* root;
  74. };

主要是需要处理有通配符'.'的情况,search函数修改了一下。其他跟Implement Trie一样。

十一、字符串匹配

1. Regular Expression Matching

  1. class Solution
  2. {
  3. public:
  4. bool isMatch(string s,string p)
  5. {
  6. return dfs(s,p,,);
  7. }
  8. bool dfs(string s,string p,int sStart,int pStart)
  9. {
  10. if(pStart==p.size()) return (sStart==s.size());
  11. if(sStart>s.size()) return false;
  12. if(p[pStart+]!='*')
  13. {
  14. if(p[pStart]==s[sStart]||(p[pStart]=='.'))
  15. return dfs(s,p,sStart+,pStart+);
  16. return false;
  17. }
  18. else
  19. {
  20. while(s[sStart]==p[pStart]||(p[pStart]=='.'&& sStart<s.size()))
  21. {//相当于将x*替代为0~n个x
  22. if(dfs(s,p,sStart,pStart+)==true)
  23. return true;
  24. sStart++;
  25. }
  26. return dfs(s,p,sStart,pStart+);//相当于将x*替代为0个x
  27. }
  28. }
  29. };

主要是题意别理解错。正则表达式(Regex)和通配符(Wildcard)并不一样。通配符里的*是可以代表任意数量任意字符,而正则表达式里的*是指的*前面的字符可以连续重复使用任意次。正则表达式里的“.*”才等价于通配符里的“*”。

注意Line20最后,需要判断sStart<s.size()。

还有DP解法。在DP部分重做一遍。

2. Wildcard Matching

  1. class Solution
  2. {
  3. public:
  4. bool isMatch(string s,string p)
  5. {
  6. return dfs(s,p,,);
  7. }
  8. bool dfs(string s,string p,int sStart,int pStart)
  9. {
  10. if(pStart==p.size()) return (sStart==s.size());
  11. if(sStart>s.size()) return false;
  12. if(p[pStart]!='*')
  13. {
  14. if(p[pStart]==s[sStart]||p[pStart]=='?')
  15. return dfs(s,p,sStart+,pStart+);
  16. return false;
  17. }
  18. else
  19. {
  20. while(sStart<s.size())
  21. {
  22. if(dfs(s,p,sStart,pStart+)==true) return true;
  23. sStart++;
  24. }
  25. return dfs(s,p,sStart,pStart+);
  26. }
  27. }
  28. };

参照上题模式来做。用dfs会TLE。但是对于面试应该可以用。

关于Line25存在的意义(不存在肯定不行,会返回一个乱七八糟的整数。因为运行到这里时没有合适的return语句,就会返回一个随便什么数。)思考一下这里的Line25以及上一道题里的。

我认为Line20 & 25可以替换为:

  1. while (sStart <= s.size())
  2. {
  3. if (dfs(s, p, sStart, pStart + ) == true) return true;
  4. sStart++;
  5. }
  6. return false;

因为Line25之所以执行是因为前面的while语句一直到循环结束都没有符合要求的情况出现。原来的代码中,while循环是while (sStart < s.size()),因此当sStart等于s.size()时,就会执行Line25的内容。由于sStart==s.size()时仍是可能符合条件的(如果此时pStart也等于p.size()的话),因此直接return dfs(s,p,sStart,pStart+1)去判断是否符合条件即可。如果把while循环改为while (sStart <= s.size()),退出循环时是sStart==s.size()+1时,此时必然不可能符合要求了。因此Line25就直接return false。

以上属猜测。目测是对的。有机会找别的oj检验一下。

关于非递归做法,参考此答案。回头做一下。

十二、

1. Word Search

  1. class Solution
  2. {
  3. public:
  4. bool exist(vector<vector<char>> &board,string word)
  5. {
  6. int m=board.size(),n=board[].size();
  7. vector<vector<bool>> visited(m,vector<bool>(n,false));
  8. for(int i=;i<m;i++)
  9. {
  10. for(int j=;j<n;j++)
  11. {
  12. if(dfs(board,word,i,j,visited,))
  13. return true;
  14. }
  15. }
  16. return false;
  17. }
  18. bool dfs(vector<vector<char>> &board,string word,int x,int y,vector<vector<bool>> &visited,int index)
  19. {
  20. if(index==word.size()) return true;
  21. if(x<||y<||x>=board.size()||y>=board[].size()) return false;
  22. if(visited[x][y]) return false;
  23. if(board[x][y]!=word[index]) return false;
  24. visited[x][y]=true;
  25. bool result=dfs(board,word,x-,y,visited,index+)||dfs(board,word,x+,y,visited,index+)||
  26. dfs(board,word,x,y-,visited,index+)||dfs(board,word,x,y+,visited,index+);
  27. visited[x][y]=false;
  28. return result;
  29. }
  30. };

refer to soulMach.

TO BE CONTINUED

2. Word Search II

3. Word Break II

4. Word Ladder II

leetcode Ch3-DFS & Backtracking II的更多相关文章

  1. LeetCode:路径总和II【113】

    LeetCode:路径总和II[113] 题目描述 给定一个二叉树和一个目标和,找到所有从根节点到叶子节点路径总和等于给定目标和的路径. 说明: 叶子节点是指没有子节点的节点. 示例:给定如下二叉树, ...

  2. LeetCode Single Number I / II / III

    [1]LeetCode 136 Single Number 题意:奇数个数,其中除了一个数只出现一次外,其他数都是成对出现,比如1,2,2,3,3...,求出该单个数. 解法:容易想到异或的性质,两个 ...

  3. [array] leetcode - 40. Combination Sum II - Medium

    leetcode - 40. Combination Sum II - Medium descrition Given a collection of candidate numbers (C) an ...

  4. LeetCode 137. Single Number II(只出现一次的数字 II)

    LeetCode 137. Single Number II(只出现一次的数字 II)

  5. LeetCode:组合总数II【40】

    LeetCode:组合总数II[40] 题目描述 给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合. candi ...

  6. leetcode@ [126] Word Ladder II (BFS + 层次遍历 + DFS)

    https://leetcode.com/problems/word-ladder-ii/ Given two words (beginWord and endWord), and a diction ...

  7. Leetcode总结之Backtracking

    本文我们就Leetcode中的一个类型的题目backtracking进行一系列的总结和归纳.backtracking这个方法本质是建立在递归的基础上,不断尝试新的路径,这里关键是每次尝试完以后需要退回 ...

  8. LeetCode:Word Ladder I II

    其他LeetCode题目欢迎访问:LeetCode结题报告索引 LeetCode:Word Ladder Given two words (start and end), and a dictiona ...

  9. LeetCode: Word Break I && II

    I title: https://leetcode.com/problems/word-break/ Given a string s and a dictionary of words dict, ...

随机推荐

  1. MySQL 重命名数据库

    首先创建目标库 create database trgdb; 获取所有源库的表名 use information_schema; select table_name from TABLES where ...

  2. Hive的Shell里hive> 执行操作时,出现FAILED: Execution Error, return code 1 from org.apache.hadoop.hive.ql.exec.DDLTask错误的解决办法(图文详解)

    不多说,直接上干货! 这个问题,得非 你的hive和hbase是不是同样都是CDH版本,还是一个是apache版本,一个是CDH版本. 问题详情 [kfk@bigdata-pro01 apache-h ...

  3. VisualSVN Server提供程序无法执行所尝试的操作 0x80041024

    VisualSVN安装后没有提供VisualSVN Server Manager的快捷方式,如下图: 可以在安装目录的bin文件夹下找到VisualSVN Server.msc,添加快捷方式.建议Vi ...

  4. Django获取Header中的信息

    今天需要从header中获取一些信息,查了一些资料,需要注意一下几点: request.META.get("header key") 用于获取header的信息 注意的是heade ...

  5. 2-7 js基础-ajax封装

    function json2url(json) { var arr = []; for (var name in json) { arr.push(name+'='+encodeURIComponen ...

  6. Mysql中的分页处理

    先来说一下Mysql中limit的语法: --语法: SELECT * FROM table LIMIT [offset,] rows | rows OFFSET offset --举例: selec ...

  7. 使用 Flask 框架写用户登录功能的Demo时碰到的各种坑(三)——使用Flask-Login库实现登录功能

    使用 Flask 框架写用户登录功能的Demo时碰到的各种坑(一)——创建应用 使用 Flask 框架写用户登录功能的Demo时碰到的各种坑(二)——使用蓝图功能进行模块化 使用 Flask 框架写用 ...

  8. RichTextBox控件

    RichTextBox控件允许用户输入和编辑文本的同时提供了比普通的TextBox控件更高级的格式特征 //color在c#中是个枚举enum 蓝色按钮:在蓝色按钮注册click事件后,richtex ...

  9. android 使用图片轮播图---banner 使用

    转自:https://github.com/youth5201314/banner 使用步骤 Step 1.依赖banner Gradle dependencies{ compile 'com.you ...

  10. 腾讯企业邮箱报错 "smtp.exmail.qq.com"port 465, isSSL false

    一.报错 "smtp.exmail.qq.com" port 465, isSSL false 通过网上搜索查询一些资料,推测是邮箱的配置出问题了. 二.修改邮箱配置 // 创建属 ...