Leetcode刷题防忘录(二)

目录

add-binary(字符串,简单,二进位)

multiply-strings(中等,字符串,二进位)

Count-and-say(简单,字符串)

gas-station(中等,贪心算法)

palindrome-partitioning-ii(困难,动态规划)


add-binary(字符串,简单,二进位)

题目

给定两个二进制字符串,返回他们的和(用二进制表示)。输入为非空字符串且只包含数字 1 和 0。

示例 1:

输入: a = "11", b = "1"
输出: "100"
示例 2:

输入: a = "1010", b = "1011"
输出: "10101"

 

思路

关键点:

1.int 和string之间的转换,ASC码48就是'0',也就是说'0'的值是48,而后依次是'1'到'9'。 这样正好是char型减去48就是它对应的int值。这么就很巧妙了,代码中的例子:

a[j-1] = a[j-1] + 1;这个好像特殊似的,可以直接加一,但实际上呢,a[j-1]的确是字符串里面的值不假,不是整形,但当asc码加上1,也的确是所要的结果,所以,就不需要再减去‘0’了。

还有这个,a[j] = a[j] - '0' + b[j];看起来两个字符串执行加减,需要都转换为两个int类型,其实只需要转换一个,只不过是将原来的:(a-48(string转int) + b-48(string转int) )+ 48(将得到的int值再转为string) 直接变成了:((a-48)+ b)取得的效果是一样的。

所以,要理解string和int是转为asc码之后的底层运算的原理,才可以。

2.最后一步,要单独处理,所以for循环中,j>0而不是j>=0。

3.string要是想在开头插入一个字符,直接插就行了: a = '0' + a;

4.while循环里面,是前++,不是后++,说明,是先加1再循环。结果一样吧。

class Solution {
public:
    string addBinary(string a, string b) {
        int al = a.size();
        int bl = b.size();
        while(al < bl) //让两个字符串等长,若不等长,在短的字符串前补零,否则之后的操作会超出索引
        {
            a = '0' + a;
            ++ al;
        }
        while(al > bl)
        {
            b = '0' + b;
            ++ bl;
        }
        for(int j = a.size() - 1; j > 0; -- j) //从后到前遍历所有的位数,同位相加
        {
            a[j] = a[j] - '0' + b[j];
            if(a[j] >=  '2') //若大于等于字符‘2’,需要进一
            {
                a[j] = (a[j] - '0') % 2 + '0';
                a[j-1] = a[j-1] + 1;
            }
        }
        //最后一步单独处理
        a[0] = a[0] - '0' + b[0]; //将ab的第0位相加
        if(a[0] >= '2') //若大于等于2,需要进一
        {
            a[0] = (a[0] - '0') % 2 + '0';
            a = '1' + a;
        }
        return a;
    }
};

multiply-strings(中等,字符串,二进位)

题目

给定两个以字符串形式表示的非负整数 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)或直接将输入转换为整数来处理。

思路

这个上面这道思路一样,只不过分成了先位与位相乘,再相加,加完考虑进位。一定要明白,每一步是位与位的关系,而不是乘数的一位对应下个整个乘数的关系。

最后,用了res.substr(i),很是巧妙,意思是从res的第i位起开始输出。用 这种方法,防止得数的开头出现零。

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);//获得字符串s中从第i位开始的字符串
		}
		return "0";


	}
};

Count-and-say(简单,字符串)

题目

报数序列是一个整数序列,按照其中的整数的顺序进行报数,得到下一个数。其前五项如下:

1.     1
2.     11
3.     21
4.     1211
5.     111221
1 被读作  "one 1"  ("一个一") , 即 11。
11 被读作 "two 1s" ("两个一"), 即 21。
21 被读作 "one 2",  "one 1" ("一个二" ,  "一个一") , 即 1211。

给定一个正整数 n(1 ≤ n ≤ 30),输出报数序列的第 n 项。注意:整数顺序将表示为一个字符串。

示例 1:

输入: 1
输出: "1"


示例 2:

输入: 4
输出: "1211"

思路

1.用上一次的结果作为这一次的输入,这个代码通过递归做到了,反复将处理完成的上一次的结果,传过来做这一次的输入。

2.to_string将int转为字符串string.

3.最后的res,是每一次都更新的,只存第n项,而不是,所有项。

class Solution {
public:
    string countAndSay(int n) {
        if(n==1) return "1";
        string strlast=countAndSay(n-1);
        int count = 1;//计数
        string res;//存放结果
        for(int i=0;i<strlast.size();i++)
        {
            if(strlast[i]==strlast[i+1])//计算有多少个相同数字
            {
                count++;
            }
            else
            {
                res+=to_string(count)+strlast[i];
                count=1;
            }
        }
       return res;
    }
};

gas-station(中等,贪心算法)

题目描述

在一条环路上有 N 个加油站,其中第 i 个加油站有汽油 gas[i] 升。你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1。

说明: 

如果题目有解,该答案即为唯一答案。输入数组均为非空数组,且长度相同。输入数组中的元素均为非负数。
示例 1:

输入: 
gas  = [1,2,3,4,5]
cost = [3,4,5,1,2]

输出: 3

解释:
从 3 号加油站(索引为 3 处)出发,可获得 4 升汽油。此时油箱有 = 0 + 4 = 4 升汽油
开往 4 号加油站,此时油箱有 4 - 1 + 5 = 8 升汽油
开往 0 号加油站,此时油箱有 8 - 2 + 1 = 7 升汽油
开往 1 号加油站,此时油箱有 7 - 3 + 2 = 6 升汽油
开往 2 号加油站,此时油箱有 6 - 4 + 3 = 5 升汽油
开往 3 号加油站,你需要消耗 5 升汽油,正好足够你返回到 3 号加油站。
因此,3 可为起始索引。
示例 2:

输入: 
gas  = [2,3,4]
cost = [3,4,3]

输出: -1

解释:
你不能从 0 号或 1 号加油站出发,因为没有足够的汽油可以让你行驶到下一个加油站。
我们从 2 号加油站出发,可以获得 4 升汽油。 此时油箱有 = 0 + 4 = 4 升汽油
开往 0 号加油站,此时油箱有 4 - 3 + 2 = 3 升汽油
开往 1 号加油站,此时油箱有 3 - 3 + 3 = 3 升汽油
你无法返回 2 号加油站,因为返程需要消耗 4 升汽油,但是你的油箱只有 3 升汽油。
因此,无论怎样,你都不可能绕环路行驶一周。

思路

1.非常困扰的问题,会不会正确的起点,出现在tank加和的过程中呢?如果,tank第一次加和大于零,第二次是正确的起点,然后,然后,后面的某一部归到了负数,那么正确的起点不就被湮没了吗?我一直在困扰这种情况,但想通了之后,就明白了,这种情况是不存在的,假设当前点是正确起点,那自起点往后根本不会出现加和为负的情况(如果出现,那当前点肯定不是),如果这个正确起点前面也出现了tank为正的数,这数也肯定是正确起点(正确起点不唯一),你想想,如果当前点所产生的余量都够走一圈了,那上一个点的余量再加上当前点的余量不是更可以走一圈了吗?所以,不存在把正确点给湮没的情况,而且,如果tank累加为负了,那走过的所有点都不可能是正确节点了。

class Solution {
public:
    int canCompleteCircuit(vector<int> &gas, vector<int> &cost) {
            int start(0),total(0),tank(0);
        for(int i=0;i<gas.size();i++){
            if((tank=tank+gas[i]-cost[i])<0){
                start=i+1;
                total+=tank;
                tank=0;
            }
        }
        return (total+tank<0)?-1:start;
    }
};

palindrome-partitioning-ii(困难,动态规划)

题目描述

给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。

返回符合要求的最少分割次数。

示例:

输入: "aab"             输出: 1
解释: 进行一次分割就可将 s 分割成 ["aa","b"] 这样两个回文子串。

思路

要切割最少的回文串,就要清楚回文串的定义,如果以某个字符为中心(字符数量为奇数),它前面的和它后面的相等,那就是回文串;如果(字符数量为偶数),它后面的和它自身相等,也是回文串。

接下来就开始动态规划,得字符数量为l,设置一个list,长度为l+1,list[i]代表前i个字符可以被划分几次,初始值设成list[i]=i-1;表示,前i个字符串至多可以被划分为i-1个。

接下来进入循环,遍历每个字符,并以当前遍历的字符为中心,分奇数和偶数两种情况找回文,如果找到了一个回文序列,就

list[end+1] = min(list[end+1], list[start]+1);假设“aab”这个序列,start=0,end=1时候,发现回文了,那么list[2]就存放的是list[0]+1,因为原先list[2]存放2,现在发现因为发现了回文,只需要切一次就行了,存放1,(list[0]+1),很巧妙!。这算是一个规律吧。然后,保存起来,因为是统计拢共的回文串切分,所以,需要在每次循环的时候,都要把切分数更新一下(list[i+1] = min(list[i+1], list[i] + 1); ),将上一次获得的结果给继承下来。

这里还有一个边界问题比较巧妙。当循环的时候设置的list的长度是(l+1),但实际的主for循环里面的上限是l,这是因为list[0]是个没用的量,真正有意义的是从list[1]开始,表示一个字符的时候切分数为0,而s还是从0开始计算,真正的末尾是l-1,所以,上限是l这个设定是没问题的。另外,也是对于S而言的。l-1确实就是对应最后一个元素了。而list对应的最后一个元素应该是list[l]。

            if(i == l-1){ // 最后一个了没必要找了
                break;
            }

 最后,地方:

            // 如果整个串都是回文串,那么就中断
            if(list[l] == 0){
                return 0;
            }

其实是多余的,不加这个也行。

 

 

class Solution {
public:
    int minCut(string s) {
        int l = s.length();
        vector<int> list(l+1); // list[i]代表前i个字符需要划几次,特别地,list[0]=-1
        for(int i = 0;i < l+1;++i){ // 初始化[-1, 0, 1, 2, 3...]
            list[i] = i - 1;
        }
        for(int i = 0;i < l;++i){ // 以每个字符为中心找最长回文子串
            list[i+1] = min(list[i+1], list[i] + 1); // 初始化,最坏情况下就比左边的多划一次
            if(i == l-1){ // 最后一个了没必要找了
                break;
            }
            // 先找偶数个的
            int start = i, end = i+1;
            while(s[start] == s[end]){
                list[end+1] = min(list[end+1], list[start]+1);
                if(end == l-1 || start == 0){
                    break;
                }
                --start, ++end;
            }
            // 再找奇数个的
            start = i-1, end = i+1;
            if(start < 0){
                continue;
            }
            while(s[start] == s[end]){
                list[end+1] = min(list[end+1], list[start]+1);
                if(end == l-1 || start == 0){
                    break;
                }
                --start, ++end;
            }
            // 如果整个串都是回文串,那么就中断
            if(list[l] == 0){
                return 0;
            }
        }
        return list[l];
    }
};

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值