每日一练(day04)

Posted 'or 1 or 不正经の泡泡

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了每日一练(day04)相关的知识,希望对你有一定的参考价值。

前言

老规矩随便来几题。

题目

买卖股票的最佳时机

给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。

你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。

返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。

示例 1:

输入:[7,1,5,3,6,4] 输出:5 解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。 示例 2:

输入:prices = [7,6,4,3,1] 输出:0 解释:在这种情况下, 没有交易完成, 所以最大利润为 0。

这个题目没啥好说的,说人话就是叫你从左到右看看两个数字之间的最大差值。
两个想法嘛,一个暴力求解,一个就是获取最小数字然后在减去求差值,这个扫一遍就好了。

class Solution 
    public int maxProfit(int prices[]) 
      //其实就是从左往右数组当中两个数字的最大差值
        int min = prices[0];
        int maxstreet = 0;
        for(int i=1;i<prices.length;i++)
            if(prices[i]<min)
                min = prices[i];
            
            else if (prices[i] - min>maxstreet)
                maxstreet = prices[i] - min;
            
        
        return maxstreet;
    


秘钥格式化

有一个密钥字符串 S ,只包含字母,数字以及 ‘-’(破折号)。其中, N 个 ‘-’ 将字符串分成了 N+1 组。

给你一个数字 K,请你重新格式化字符串,使每个分组恰好包含 K 个字符。特别地,第一个分组包含的字符个数必须小于等于 K,但至少要包含 1
个字符。两个分组之间需要用 ‘-’(破折号)隔开,并且将所有的小写字母转换为大写字母。

给定非空字符串 S 和数字 K,按照上面描述的规则进行格式化。

示例 1:

输入:S = “5F3Z-2e-9-w”, K = 4 输出:“5F3Z-2E9W” 解释:字符串 S 被分成了两个部分,每部分 4
个字符;
注意,两个额外的破折号需要删掉。 示例 2:

输入:S = “2-5g-3-J”, K = 2 输出:“2-5G-3J” 解释:字符串 S 被分成了 3 个部分,按照前面的规则描述

这里只说一下就是那个注意点。
我们这个其实是从后面开始的,也就是从后面保证例如
S = “2-5g-3-J”, K = 2 输出:“2-5G-3J” 如果从前面开始 那就是 25-G3-J

所以我们得从后面开始,而不是从前面开始扫描。

class Solution 
    public String licenseKeyFormatting(String s, int k) 

        StringBuilder res = new StringBuilder();
        int ki = 0;
        for(int i=s.length()-1;i>=0;i--)
            if(s.charAt(i)!='-')
                res.append(Character.toUpperCase(s.charAt(i)));
                ki++;
                if(ki % k==0)
                    res.append("-");
                
            
        
        if(res.length() > 0 && res.charAt(res.length()-1) == '-')
            res.deleteCharAt(res.length()-1);
        


        return res.reverse().toString();
    

来 String StringBuffer StringBuilder 之间有什么区别。

验证回文

给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。

说明:本题中,我们将空字符串定义为有效的回文串。

示例 1:

输入: “A man, a plan, a canal: Panama” 输出: true
解释:“amanaplanacanalpanama” 是回文串 示例 2:

输入: “race a car” 输出: false 解释:“raceacar” 不是回文串

这个没什么好说的,前面我们也做过类似的,而且题目还没说不能使用自带的函数来解题
如果可以直接把字符串过滤然后反转对比

class Solution 
    public boolean isPalindrome(String s) 
        StringBuffer res = new StringBuffer();
        int length = s.length();
        for (int i = 0; i < length; i++) 
            char ch = s.charAt(i);
            if (Character.isLetterOrDigit(ch)) 
                res.append(Character.toLowerCase(ch));
            
        
        StringBuffer res_rev = new StringBuffer(sgood).reverse();
        return res.toString().equals(res_rev.toString());
    


那这里我们还可以使用双指针。但是这里的话我认为我们其实是没有必要去生成一个新的字符串来进行过滤的,我们其实可以直接在字符串s里面使用双指针判断,这里只需要注意越界就可以,就不需要浪费内存空间。

class Solution 
    public boolean isPalindrome(String s) 
        boolean flag = true;
        if(s.length() == 0)
            return true;
        
        for(int i=0,j=s.length()-1;i<s.length()&&j>=0;i++,j--)
            while (!Character.isLetterOrDigit(s.charAt(i)))
                i++;
                if(i>=s.length())
                    i--;
                    break;
                
            
            while (!Character.isLetterOrDigit(s.charAt(j)))
                j--;
                if(j<0)
                    j++;
                    break;
                
            
            if(Character.isLetterOrDigit(s.charAt(i))&&Character.isLetterOrDigit(s.charAt(j)))
                if (Character.toLowerCase(s.charAt(i)) != Character.toLowerCase(s.charAt(j))) 
                    flag = false;
                    break;
                

        

        return flag;
    


两数相加

给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。

请你将两个数相加,并以相同形式返回一个表示和的链表。

你可以假设除了数字 0 之外,这两个数都不会以 0 开头。

输入:l1 = [2,4,3], l2 = [5,6,4]
输出:[7,0,8]
解释:342 + 465 = 807.
示例 2:

输入:l1 = [0], l2 = [0] 输出:[0]

乍一看都笑了,直接把链表里面的数据拿出来然后相加是吧

class Solution 
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) 
        ListNode head = null, tail = null;
        int carry = 0;
        while (l1 != null || l2 != null) 
            int n1 = l1 != null ? l1.val : 0;
            int n2 = l2 != null ? l2.val : 0;
            int sum = n1 + n2 + carry;
            if (head == null) 
                head = tail = new ListNode(sum % 10);
             else 
                tail.next = new ListNode(sum % 10);
                tail = tail.next;
            
            carry = sum / 10;
            if (l1 != null) 
                l1 = l1.next;
            
            if (l2 != null) 
                l2 = l2.next;
            
        
        if (carry > 0) 
            tail.next = new ListNode(carry);
        
        return head;
    


但是这里注意 int 是有范围的,我们的数组很长,所以这样是不行的,但是这里又说了,是逆序也就是从个位开始,所以我们就直接模拟计算机加减法嘛。

这个思路呢就是

class Solution 
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) 
        ListNode res = null, r = null;
        int Jinwei = 0;
        while (l1 != null || l2 != null) 

            int n1 = (l1 != null ? l1.val : 0);
            int n2 = (l2 != null ? l2.val : 0);
            int sum = n1 + n2 + Jinwei;
            Jinwei = sum / 10;

            if (res == null) 
                res = r = new ListNode(sum % 10);
             else 
                r.next = new ListNode(sum % 10);
                r = r.next;
            

            if (l1 != null) 
                l1 = l1.next;
            
            if (l2 != null) 
                l2 = l2.next;
            
        
        if (Jinwei > 0) 
            r.next = new ListNode(Jinwei);
        
        return res;
    


最长回文子串

给你一个字符串 s,找到 s 中最长的回文子串。

示例 1:

输入:s = “babad” 输出:“bab” 解释:“aba” 同样是符合题意的答案。 示例 2:

输入:s = “cbbd” 输出:“bb” 示例 3:

输入:s = “a” 输出:“a” 示例 4:

输入:s = “ac” 输出:“a”

这个的话就需要好好用到我们那个回文串的特点了。
特点:

1.从左到右和从右到左的字符串相等 abccba
2.舍去左右对等的部分依然是回文串 abccba --> bccb
3.从中心开始扩散 aa --> baab , c–> bab

知道这三点我们基本上就可以开始解题目了

1.获取到我们字符串当中的回文串
2.假设某一个字符就是回文串的中心串,然后按照特点三去扩张判断
3.由于不知道回文子串是奇数还是偶数,所以需要扫描两次判断较长的
4.如果发现当前这一次的长度大于上一次的长度,那么更新我们的起始位置(具体看代码)

class Solution 
    public String longestPalindrome(String s) 
        if (s == null || s.length() < 1) 
            return "";
        
        int start = 0, end = 0;
        for (int i = 0; i < s.length(); i++) 
            int len1 = ExpandScanning(s, i, i);//奇数扫描,我们假设当前的就是中心点从左到右
            int len2 = ExpandScanning(s, i, i + 1); //偶数扫描,我们不知道回文串到底是奇数还是偶数

            int len = Math.max(len1, len2);
            if (len > end - start) 
                //判断当前的长度是不是彼上一次的长,是的话就更新
                start = i - (len - 1) / 2;
                //如果是奇数个 start  = i(中心点) - (len-1)/2
                //如果是偶数个 start = i(中心点) - (len -2)/2
                //但是由于 len 是 int 所以 (len -1 or -2) /2 效果一样
                end = i + len / 2;
                //如果是奇数 end = i+(len-1)/2
                //如果是偶数 end = i+len/2
                //同理这个也是一样的

            
        
        return s.substring(start, end + 1);
    

    public int ExpandScanning(String s, int i, int j) 
        while (i >= 0 && j < s.length() && s.charAt(i) == s.charAt(j)) 
            --i;
            ++j;
        
        return j - i - 1;
    


以上是关于每日一练(day04)的主要内容,如果未能解决你的问题,请参考以下文章

每日一练(day01)

每日一练(day02 手撕 KMP)

每日一练(day12&PriorityQueue)

每日一练(day05)

每日一练(day03--动态规划dp)

每日一练(day10)