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]);          
           
        }
    }
};

 

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