• leetcode Ch3-DFS & Backtracking II


    一、Gray Code

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

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

    需要注意,在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, 0, 0, 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 = 0; i < brackets.size(); i++) {
    17             if ((i == 1 && left <= right) || (left == n && i == 0)) {
    18                 continue;
    19             }
    20             tmp += brackets[i];
    21             if (i == 0) {
    22                 dfs(res, tmp, n, left + 1, right, brackets);
    23             } else {
    24                 dfs(res, tmp, n, left, right + 1, brackets);
    25             }
    26             tmp.pop_back();
    27         }
    28     }
    29 };
    View Code

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

    写的有点繁琐。

     1 class Solution
     2 {
     3 public:
     4     vector<string> generateParenthesis(int n)
     5     {
     6         if(n==0) return result;
     7         finalPos=2*n;
     8         left=n;
     9         right=n;
    10         dfs(0);        
    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>0 && right>0)
    21         {
    22             right--;
    23             dif--;
    24             tmp+=')';
    25             dfs(n+1);
    26             tmp.resize(tmp.size()-1);
    27             right++;
    28             dif++;
    29         }
    30         if(left>0)
    31         {
    32             left--;
    33             dif++;
    34             tmp+='(';    
    35             dfs(n+1);    
    36             tmp.resize(tmp.size()-1);
    37             left++;
    38             dif--;
    39         }
    40     }
    41     int finalPos;
    42     int dif=0;
    43     string tmp;
    44     int left;
    45     int right;
    46     vector<string> result;
    47 };
    View Code

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

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

    三、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=0;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 };
    View Code

    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=0;i<nums.size();i++)
     9             umap[nums[i]]=1;
    10         helper(result,umap,0);
    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==1)
    23             {
    24                 tmp.push_back(it->first);    
    25                 umap[it->first]=2;
    26                 helper(result,umap,n+1);
    27                 umap[it->first]=1;
    28                 tmp.resize(tmp.size()-1);        
    29             }    
    30         }
    31     }
    32     int len;
    33     unordered_map<int,int> umap;
    34     vector<int> tmp;
    35 };
    View Code

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

    较简洁的做法

     1 class Solution {
     2 public:
     3     vector<vector<int> > permute(vector<int> &num) {
     4         vector<vector<int> > result;
     5         permuteRecursive(num, 0, 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 + 1, result);
    16             swap(num[begin], num[i]);
    17         }
    18     }
    19 };
    View Code

     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>0)
    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 };
    View Code

    注意老是会忘刚开始时初始化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, 1);
     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+ 1);//dfs(n, k, start + 1);
    24             path.pop_back();
    25         }
    26     }
    27 };
    View Code

    在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,0);
     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+1);
    19             path.pop_back();
    20         }
    21     }
    22 };
    View Code

    该模板化版本和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,0);        
     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+1);
    21         path.pop_back();
    22         dfs(nums,step+1);        
    23     }
    24 };
    View Code

    一开始打算利用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(1,vector<int>());
     7         sort(nums.begin(),nums.end());
     8         for(int i=0;i<nums.size();i++)
     9         {
    10             int n=result.size();
    11             for(int j=0;j<n;j++)
    12             {
    13                 result.push_back(result[j]);
    14                 result.back().push_back(nums[i]);
    15             }
    16         }
    17         return result;
    18     }
    19 };
    View Code

    基本思路就是每次循环都把当前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,0);
     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-1]) continue;
    18             path.push_back(nums[i]);
    19             dfs(nums,i+1);
    20             path.pop_back();
    21         }
    22     }
    23 };
    View Code

    在产生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 }
    View Code

    回头去看,该算法就是保证了相同深度的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,0,0,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 };
    View Code

    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,0,0,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-1]) continue;
    23             path.push_back(nums[i]);
    24             dfs(nums,i+1,sum+nums[i],target);
    25             path.pop_back();
    26         }
    27     }
    28 };
    View Code

    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={1,2,3,4,5,6,7,8,9};
     7         dfs(nums,k,n,0,0);
     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+1,sum+nums[i]);
    24             path.pop_back();    
    25         }
    26     }
    27 };
    View Code

    判断的时候多了个限制条件,需要既让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(0,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+1)))
    21             {
    22                 path.push_back(s.substr(start,i-start+1));
    23                 dfs(i+1,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=0;i<len/2;i++)
    33         {
    34             if(s[i]!=s[len-i-1])
    35                 return false;
    36         }
    37         return true;
    38     }
    39 };
    View Code

    注意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,0);
     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]-'0')];
    23         for(int i=0;i<tmp.size();i++)
    24         {
    25             path.push_back(tmp[i]);
    26             dfs(s,step+1);
    27             path.pop_back();
    28         }
    29     }
    30     void initialization()
    31     {
    32         umap[2]="abc";
    33         umap[3]="def";
    34         umap[4]="ghi";
    35         umap[5]="jkl";
    36         umap[6]="mno";
    37         umap[7]="pqrs";
    38         umap[8]="tuv";
    39         umap[9]="wxyz";
    40         umap[0]=" ";
    41     }
    42 };
    View Code

     注意: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("123456789");    
     8         string str=s.substr(0,n);
     9         k--;
    10         for(int i=0;i<n;i++)
    11         {
    12             int tmp=factorial(str.size()-1);    
    13             int q=k/tmp;
    14             result[i]=str[q];
    15             str.erase(q,1);
    16             k-=tmp*q;
    17         }
    18         return result;
    19     }
    20     int factorial(int n)
    21     {
    22         if(n==0) return 1;
    23         int result=1;
    24         for(int i=2;i<=n;i++)
    25             result*=i;
    26         return result;
    27     }
    28 };
    View Code

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

    八、Sudoku

    1. Sudoku Solver

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

     注意一些技巧。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(9,false);
     6         for(int i=0;i<9;i++)
     7         {
     8             fill(mark.begin(),mark.end(),false);
     9             for(int j=0;j<9;j++)
    10             {
    11                 if(board[i][j]=='.') continue;
    12                 if(mark[board[i][j]-'1']==true) return false;
    13                 mark[board[i][j]-'1']=true;
    14             }    
    15         }
    16         for(int j=0;j<9;j++)
    17         {
    18             fill(mark.begin(),mark.end(),false);
    19             for(int i=0;i<9;i++)
    20             {
    21                 if(board[i][j]=='.') continue;
    22                 if(mark[board[i][j]-'1']==true) return false;
    23                 mark[board[i][j]-'1']=true;
    24             }
    25         }
    26         for(int i=0;i<9;i+=3)
    27         {
    28             for(int j=0;j<9;j+=3)
    29             {
    30                 fill(mark.begin(),mark.end(),false);
    31                 for(int k=0;k<9;k++)
    32                 {
    33                     int row=i+k/3;
    34                     int col=j+k%3;
    35                     if(board[row][col]=='.') continue;
    36                     if(mark[board[row][col]-'1']==true) return false;
    37                     mark[board[row][col]-'1']=true;
    38                 }    
    39             }
    40         }
    41         return true;
    42     }
    43 };
    View Code

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

    九、Restore IP Addresses

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

    第一遍做错了。除了标记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(0){}
     9     TrieNode(char ch):content(ch),isend(false),shared(0){}
    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 };
    View Code

    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(0),shared(0){}
    15     TrieNode(char ch):content(ch),isend(0),shared(0){}
    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[0]=='.')
    51         {
    52             for(auto child:root->children)
    53                 if(dfs(word.substr(1,word.size()-1),child)==true)
    54                     return true;
    55             return false;
    56         }
    57         else
    58         {
    59             TrieNode* curr=root->subNode(word[0]);
    60             if(curr==NULL) return false;    
    61             else return dfs(word.substr(1,word.size()-1),curr);    
    62         }
    63     }
    64     WordDictionary()
    65     {
    66         root=new TrieNode();
    67     }
    68     ~WordDictionary()
    69     {
    70         delete root;
    71     }
    72 private:
    73     TrieNode* root;
    74 };
    View Code

    主要是需要处理有通配符'.'的情况,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,0,0);    
     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+1]!='*')
    13             {
    14                 if(p[pStart]==s[sStart]||(p[pStart]=='.')) 
    15                     return dfs(s,p,sStart+1,pStart+1);
    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+2)==true)
    23                         return true;
    24                     sStart++;
    25                 }
    26                 return dfs(s,p,sStart,pStart+2);//相当于将x*替代为0个x
    27             }
    28         }
    29     };
    View Code

    主要是题意别理解错。正则表达式(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,0,0);
     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+1,pStart+1);
    16             return false;
    17         }
    18         else
    19         {
    20             while(sStart<s.size())
    21             {
    22                 if(dfs(s,p,sStart,pStart+1)==true) return true;
    23                 sStart++;
    24             }
    25             return dfs(s,p,sStart,pStart+1);
    26         }
    27     }
    28 };
    View Code

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

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

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

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

     因为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[0].size();
     7         vector<vector<bool>> visited(m,vector<bool>(n,false));
     8         for(int i=0;i<m;i++)
     9         {    
    10             for(int j=0;j<n;j++)
    11             {
    12                 if(dfs(board,word,i,j,visited,0)) 
    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<0||y<0||x>=board.size()||y>=board[0].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-1,y,visited,index+1)||dfs(board,word,x+1,y,visited,index+1)||
    26             dfs(board,word,x,y-1,visited,index+1)||dfs(board,word,x,y+1,visited,index+1);
    27         visited[x][y]=false;
    28         return result;
    29     }
    30 };
    View Code

    refer to soulMach.

    TO BE CONTINUED

    2. Word Search II

    3. Word Break II

    4. Word Ladder II

  • 相关阅读:
    iOS:分组的表格视图UITableView,可以折叠和展开
    iOS:带主标题、副标题、图像类型的表格视图UITableView
    iOS:多个单元格的删除(方法二):
    iOS:多个单元格的删除(方法一)
    iOS:UITableViewCell自定义单元格
    iOS:删除、插入、移动单元格
    iOS:UITableView表格视图控件
    iOS:UIImageView图像视图控件
    iOS:UIScrollView控件和UIPageControl控件的详解
    淘宝卖家工具推荐
  • 原文地址:https://www.cnblogs.com/forcheryl/p/4505067.html
Copyright © 2020-2023  润新知