leetcode解题思路分析(七)43-49题

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

本题其实就是竖式计算,但是有个地方可以优化:m位乘以n位最多只有m + n位,而单独位相乘则最多两位,因此可以拆分为多个两位相乘并累加上一位

class Solution {
public:
    string multiply(string num1, string num2) {
        int n1=num1.size();
        int n2=num2.size();
        string res(n1 + n2, '0');
        for(int i = n2 - 1; i >= 0; i--)
        {
            for(int j = n1 - 1; j >= 0; j--)
            {
                int temp = (res[i + j + 1] - '0') + (num1[j] - '0') * (num2[i] - '0');
                res[i + j + 1] = temp % 10 + '0';//当前位
                res[i + j ] += temp / 10; //前一位加上进位,res[i+j]已经初始化为'0',加上int类型自动转化为char,所以此处不加'0'
            }
        }
        
        //去除首位'0'
        for(int i = 0; i < n1 + n2; i++)
        {
            if(res[i] != '0')
                return res.substr(i);
        }
        return "0";
    
    }
};
  1. 通配符匹配
    给定一个字符串 (s) 和一个字符模式 § ,实现一个支持 ‘?’ 和 ‘*’ 的通配符匹配。
    ‘?’ 可以匹配任何单个字符。
    *’ 可以匹配任意字符串(包括空字符串)。
    两个字符串完全匹配才算匹配成功。

本题中?其实不用在意,s和p指针自增略过即可,但是*需要着重考虑,因为可以代替任意长度。这里有一个容易出现的误区,如果匹配让*后一个字符等于s中判断位置之后第一个值则会出现错误。所以这里需要记录星号所在位置,然后继续自增s匹配下一处相同的值

class Solution {
public:
    bool isMatch(string s, string p) {
        int i = 0, j = 0, iStar = -1, jStar = -1, m = s.size(), n = p.size();
        while (i < m) {
            if (j < n && (s[i] == p[j] || p[j] == '?')) {
                ++i, ++j;
            } else if (j < n && p[j] == '*') {
                iStar = i;
                jStar = j++;
            } else if (iStar >= 0) {
                i = ++iStar;
                j = jStar + 1;
            } else return false;
        }
        while (j < n && p[j] == '*') ++j;//去除多余星号
        return j == n;
    }
};


  1. 跳跃游戏
    给定一个非负整数数组,你最初位于数组的第一个位置。
    数组中的每个元素代表你在该位置可以跳跃的最大长度。
    你的目标是使用最少的跳跃次数到达数组的最后一个位置。

本题的解法大致可以算作动态规划:dp[0] = 1 + dp[1]或dp[2], … dp[dp[0]],倒推至可以一步到达n,则完成,计数在dp之中,然后取最小值返回即可

	int jump(vector<int>& nums) {
		int size = nums.size();
		int dp[100] = { 0 };

		if (size == 1)
			return 0;
		for (int i = size - 2; i >= 0; i--)
		{
			if (nums[i] + i >= size - 1)
				dp[i] = 1;
			else
			{
				int min = size + 1;
				for (int j = 1; j <= nums[i]; j++)
				{
					if (dp[i + j] >= 0 && dp[i + j] < min)
					{
						min = dp[i + j] + 1;
					}
				}
				dp[i] = min;
			}
		}

		return dp[0];
	}

第二种方法是从头往后贪心算法求解

class Solution {
public:
    int jump(vector<int>& nums) {
        int farthest=0;
        if (size(nums) <= 1) return 0;
        int *p = new int[size(nums)];
        for (int i = 0; i < size(nums); i++) 
            p[i]=INT_MAX;
        p[0]=0;
        for (int i=0;i<size(nums);i++){
            if ((i+nums[i]+1)>=size(nums)) return p[i]+1;
            else if ((i+nums[i])>farthest) {
                for (int j=farthest-i;j<=nums[i];j++){
                    if (p[i]+1<p[i+j]) p[i+j]=p[i]+1;
                }
                farthest=i+nums[i];
            }
        }
        return -1;
    }
};

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

这种问题显然采用回溯法求解,如果到最后一位则push并返回,否则求下一位

class Solution {
public:
    vector<vector<int>> permute(vector<int>& nums) {
        vector<vector<int>> res;
        backtrack(res,nums,0);
        return res;
    }
    void backtrack(vector<vector<int>> &res,vector<int> nums,int level){
        if(level==nums.size()){
            res.push_back(nums);
            return;
        } 
        for(int j=level;j<nums.size();j++){
            if(j!=level) swap(nums[level],nums[j]);
            backtrack(res,nums,level+1);
            if(j!=level) swap(nums[level],nums[j]);
        }
    }
};
  1. 全排列 II
    给定一个可包含重复数字的序列,返回所有不重复的全排列。

本题解法和上题类似,但是需要增加一个剪枝的过程。

class Solution {
public:
public:
vector<vector<int>> m_vecs;
int N;
    bool isValid(vector<int> & vec, int x)
    {
        for (auto v:vec) if(v==x) return false;
        return true;
    }
    void recur_per(vector<int>& nums, vector<int> & vec, int row)
    {
        if (row == N)
        {
            vector<int> vec2;
            for (auto v: vec)  vec2.push_back(nums[v]);
            m_vecs.push_back(vec2);
            return;
        }
        for (int i = 0; i< N; i++)
        {
            if (!isValid(vec, i)) continue;
            if (i>0 && nums[i]==nums[i-1] && isValid(vec, i-1)) continue;
            vec.push_back(i);
            recur_per(nums, vec, row+1);
            vec.pop_back();
        }
    }
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        int n = nums.size();
        if (!n) return m_vecs;
        this->N = n;
        sort(nums.begin(),nums.end());
        vector<int> vec;
        recur_per(nums, vec, 0);
        return m_vecs;
    }
};

  1. 旋转图像
    给定一个 n × n 的二维矩阵表示一个图像。将图像顺时针旋转 90 度。
    说明:
    你必须在原地旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要使用另一个矩阵来旋转图像。

直接转置矩阵然后镜像调换即可,也可以采用一个规律:
对于尺寸为n*n的矩阵,其任意(i, j)点,旋转后的坐标为(j, n - 1 - i)

class Solution {
public:
    void rotate(vector<vector<int>>& matrix) {
        for (int i=0; i<matrix.size(); i++)
            for (int j=0; j<i; j++) swap(matrix[i][j], matrix[j][i]);
        
        for (auto& row: matrix) reverse(row.begin(), row.end());
    }
};
  1. 字母异位词分组
    给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。

这种一看就知道是用哈希表进行分类处理:key为字母,value为出现次数,然后每一个和前面的进行比较,一样则放在一类,不一样则放在下一类,依次处理

class Solution {
public:    
    map<map<char,int>,vector<string> >mp;
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
        for(int i=0,len=strs.size();i<len;++i){
            map<char,int>m; // 
            for(int j=0,l=strs[i].size();j<l;++j)
                m[strs[i][j]]++;   // 该字符串字母出现次数
            mp[m].push_back(strs[i]);  //  该字母出现次数相同的map插入该字符串
        }
        vector<vector<string> >ans;
        map<map<char,int>,vector<string> >::iterator it = mp.begin();
        for(;it!=mp.end();++it)
            ans.push_back(it->second);
        return ans;
    }
};


但是这种做法最大的问题在于时间和空间效率均极低,因此如果想要优化则应采用别的方法,比如对字母进行排序然后比较,如果相同则是字母异位词。也可以对数字进行统计计数(比特位),计数相同为同一个词

发布了129 篇原创文章 · 获赞 15 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/u013354486/article/details/103944132