415字符串相加;43字符串相乘;46全排列;47全排列 II

给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和。

注意:


    num1 和num2 的长度都小于 5100.
    num1 和num2 都只包含数字 0-9.
    num1 和num2 都不包含任何前导零。
    你不能使用任何内建 BigInteger 库, 也不能直接将输入的字符串转换为整数形式。

class Solution {
public:
    string addStrings(string num1, string num2) {
        int s1=num1.size(),s2=num2.size();
        if(s1==0||s2==0)return s1==0?num2:num1;
        int i1=s1-1,i2=s2-1;
        string res="";
        int carry=0;
        while(i1>=0&&i2>=0){
            int temp=num1[i1]-'0'+num2[i2]-'0'+carry;
            if(temp>9){
                carry=1;
                res=char(temp-10+'0')+res;
            }
            else{
                carry=0;
                res=char(temp+'0')+res;
            }
            --i1,--i2;
        }
        while(i1>=0){
            int temp=num1[i1]-'0'+carry;
            if(temp>9){
                carry=1;
                res=char(temp-10+'0')+res;
            }
            else{
                carry=0;
                res=char(temp+'0')+res;
            }
            --i1;            
        }
        while(i2>=0){
            int temp=num2[i2]-'0'+carry;
            if(temp>9){
                carry=1;
                res=char(temp-10+'0')+res;
            }
            else{
                carry=0;
                res=char(temp+'0')+res;
            }
            --i2;            
        }
        return carry==1?'1'+res:res;
    }
};

给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。

示例 1:

输入: num1 = "2", num2 = "3"
输出: "6"

示例 2:

输入: num1 = "123", num2 = "456"
输出: "56088"

说明:


    num1 和 num2 的长度小于110。
    num1 和 num2 只包含数字 0-9。
    num1 和 num2 均不以零开头,除非是数字 0 本身。
    不能使用任何标准库的大数类型(比如 BigInteger)或直接将输入转换为整数来处理。

class Solution {
public:
    string multiply(string num1, string num2) {
        int s1=num1.size(),s2=num2.size();
        vector<int>resInt(s1+s2,0);//两数相乘最大位宽为s1+s2
        string resStr="";
        for(int i=s1-1;i>=0;--i)
            for(int j=s2-1;j>=0;--j){
                int mul=(num1[i]-'0')*(num2[j]-'0');
                int index1=i+j,index2=i+j+1;//num1[i]和num2[j]位相乘结果最多为两位数mul,分贝对应index1和index2位置
                mul=mul+resInt[index2];
                resInt[index2]=mul%10;
                resInt[index1]+=mul/10;//之所以不用讨论index1位置的进位情况是因为ij遍历从后往前遍历,现在的index1位置会当作后面的index2进行进位计算,只有最后的i=0,j=0位置的index1不会变成后序计算的index2位置,又因为mul结果最大为两位数,所以最后的i=0,j=0位置相乘的index1绝对不会进位
            }
        int i=0;
        while(i<resInt.size()&&resInt[i]==0)++i;//结果可能没有用到最大位宽,去掉首连续的0
        while(i<resInt.size())
            resStr.push_back(resInt[i++]+'0');
        return resStr==""?"0":resStr;//“0”+“0”的特殊情况,所以讨论resStr==“”的情况
    }
};

给定一个 没有重复 数字的序列,返回其所有可能的全排列。

示例:

输入: [1,2,3]
输出:
[
  [1,2,3],
  [1,3,2],
  [2,1,3],
  [2,3,1],
  [3,1,2],
  [3,2,1]
]

class Solution {//最初解法/模板化解法
public:
    vector<vector<int>> res;
    vector<int> path;
    vector<int> nums;
    vector<vector<int>> permute(vector<int>& nums) {
        if(nums.size()==0)return {};
        //if(nums.size()==1)return vector<vector<int>>().push_back(nums);
        this->nums=nums;
        vector<bool> covered(nums.size(),false);
        helper(0,covered);
        return res;
    }
    void helper(int count,vector<bool> &covered){
        if(count==nums.size()){
            res.push_back(path);
            return;
        }
        for(int i=0;i<nums.size();++i){
            if(!covered[i]){
                path.push_back(nums[i]);
                covered[i]=true;
                helper(count+1,covered);
                covered[i]=false;
                path.pop_back();
            }                
        }
    }
};

使用标记数组来处理填过的数是一个很直观的思路,但是可不可以去掉这个标记数组呢?毕竟标记数组也增加了我们算法的空间复杂度。
答案是可以的,我们可以将题目给定的 nnn 个数的数组 nums[]\textit{nums}[]nums[] 划分成左右两个部分,左边的表示已经填过的数,右边表示待填的数,我们在递归搜索的时候只要动态维护这个数组即可。
具体来说,假设我们已经填到第 first\textit{first}first 个位置,那么 nums[]\textit{nums}[]nums[] 数组中 [0,first−1][0,\textit{first}-1][0,first−1] 是已填过的数的集合,[first,n−1][\textit{first},n-1][first,n−1] 是待填的数的集合。我们肯定是尝试用 [first,n−1][\textit{first},n-1][first,n−1] 里的数去填第 first\textit{first}first 个数,假设待填的数的下标为 iii ,那么填完以后我们将第 iii 个数和第 first\textit{first}first 个数交换,即能使得在填第 first+1\textit{first}+1first+1个数的时候 nums[]\textit{nums}[]nums[] 数组的[0,first][0,first][0,first] 部分为已填过的数,[first+1,n−1][\textit{first}+1,n-1][first+1,n−1] 为待填的数,回溯的时候交换回来即能完成撤销操作。
举个简单的例子,假设我们有 [2, 5, 8, 9, 10] 这 5 个数要填入,已经填到第 3 个位置,已经填了 [8,9] 两个数,那么这个数组目前为  [8, 9 | 2, 5, 10] 这样的状态,分隔符区分了左右两个部分。假设这个位置我们要填 10 这个数,为了维护数组,我们将 2 和 10 交换,即能使得数组继续保持分隔符左边的数已经填过,右边的待填 [8, 9, 10 | 2, 5] 。
当然善于思考的读者肯定已经发现这样生成的全排列并不是按字典序存储在答案数组中的,如果题目要求按字典序输出,那么请还是用标记数组或者其他方法。

 

 

class Solution {//改进解法
public:
    void backtrack(vector<vector<int>>& res, vector<int>& output, int first, int len){
        if (first == len) {
            res.emplace_back(output);
            return;
        }
        for (int i = first; i < len; ++i) {
            swap(output[i], output[first]);//[0~first-1]已排序,[first,size-1]未排序
            backtrack(res, output, first + 1, len);
            swap(output[i], output[first]);
        }
    }
    vector<vector<int>> permute(vector<int>& nums) {
        vector<vector<int> > res;
        backtrack(res, nums, 0, (int)nums.size());
        return res;
    }
};

给定一个可包含重复数字的序列,返回所有不重复的全排列。

示例:

输入: [1,1,2]
输出:
[
  [1,1,2],
  [1,2,1],
  [2,1,1]
]

class Solution {
    vector<vector<int>>res;
    vector<int>nums;//兼具path和visited的功能
public:
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        if(nums.size()==0)return {};
        this->nums=nums;
        helper(0);
        return res;
    }
    void helper(int start){
        if(start==nums.size()){
            res.push_back(nums);
            return;
        }
        unordered_set<int>s;
        for(int i=start;i<nums.size();++i){
            if(s.count(nums[i]))continue;
            s.insert(nums[i]);
            swap(nums[i],nums[start]);
            helper(start+1);
            swap(nums[i],nums[start]);          
           
        }
    }
};

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章