LeetCode 6. Z 字形变换 / 564. 寻找最近的回文数 / 258. 各位相加

Posted Zephyr丶J

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode 6. Z 字形变换 / 564. 寻找最近的回文数 / 258. 各位相加相关的知识,希望对你有一定的参考价值。

6. Z 字形变换

2022.3.1 每日一题,又是新的一月了

题目描述

将一个给定字符串 s 根据给定的行数 numRows ,以从上往下、从左到右进行 Z 字形排列。

比如输入字符串为 “PAYPALISHIRING” 行数为 3 时,排列如下:

P   A   H   N
A P L S I I G
Y   I   R

之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:“PAHNAPLSIIGYIR”。

请你实现这个将字符串进行指定行数变换的函数:

string convert(string s, int numRows);

示例 1:

输入:s = “PAYPALISHIRING”, numRows = 3
输出:“PAHNAPLSIIGYIR”

示例 2:

输入:s = “PAYPALISHIRING”, numRows = 4
输出:“PINALSIGYAHRPI”
解释:

P     I    N
A   L S  I G
Y A   H R
P     I

示例 3:

输入:s = “A”, numRows = 1
输出:“A”

提示:

1 <= s.length <= 1000
s 由英文字母(小写和大写)、’,’ 和 ‘.’ 组成
1 <= numRows <= 1000

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/zigzag-conversion
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

我这里为了用这个方向标记专门这样写的

class Solution 
    public String convert(String s, int numRows) 
        /*
        //如果有5行,1              9               17
                     2          8   10          16
                     3      7       11      15
                     4  6           12  14
                     5              13
        */
        //所以呢,遍历这个字符串,到了5转弯
        if(numRows == 1)
            return s;
        String[] ss = new String[numRows];
        Arrays.fill(ss, "");
        int l = s.length();
        int idx = 0;
        int dir = 1;
        for(int i = 0; i < l; i++)
            char c = s.charAt(i);
            ss[idx] = ss[idx] + c;
            if(idx % (2 * (numRows - 1)) == 0)
                dir = 1;
            else if((idx + numRows - 1) % (2 * (numRows - 1)) == 0)
                dir = -1;
            idx += dir;
        
        String res = "";
        for(String t : ss)
            res = res + t;
        
        return res;
    

最开始写的代码是观察每一行的规律从而直接构造的,

class Solution 
    public String convert(String s, int numRows) 
        //感觉像是找规律题一样,找一下规律应该就做出来了
        //1               9               17
        //2           8   10          16  18
        //3       7       11      15      19
        //4   6           12  14
        //5               13
        //5行,所以第一行两个数相差 (5 - 1) * 2 = 8
        //第二行,6,2;第三行4,4....
        if(numRows == 1)
            return s;
        int left = (numRows - 1) * 2;
        int right = 0;
        int l = s.length();
        StringBuffer sb = new StringBuffer();
        for(int i = 0; i < numRows; i++)
            int j = i;
            if(j < l)
                sb.append(s.charAt(j)); 
            while(j < l)
                if(i != numRows - 1)
                    j += left;
                    if(j >= l)
                        break;
                    sb.append(s.charAt(j));
                
                if(i != 0)
                    j += right;
                    if(j >= l)
                        break;
                    sb.append(s.charAt(j));
                
            
            left -= 2;
            right += 2;
        
        return sb.toString();
    

564. 寻找最近的回文数

2022.3.2 每日一题

题目描述

给定一个表示整数的字符串 n ,返回与它最近的回文整数(不包括自身)。如果不止一个,返回较小的那个。

“最近的”定义为两个整数差的绝对值最小。

示例 1:

输入: n = “123”
输出: “121”

示例 2:

输入: n = “1”
输出: “0”
解释: 0 和 2是最近的回文,但我们返回最小的,也就是 0。

提示:

1 <= n.length <= 18
n 只由数字组成
n 不含前导 0
n 代表在 [1, 10^18 - 1] 范围内的整数

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/find-the-closest-palindrome
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

硬生生考虑了所有情况
提交之前没考虑100 1000 这种情况,加上以后竟然过了,还超过了百分之九十多

首先一般情况就是直接反转前面
如果直接反转前半部分后小了,那么把前面加1再反转,如果大了,减1再反转

还有特殊情况就是本身就是回文的话,需要加一减一都考虑

还有更特殊的情况就是加1减1以后长度变化了,也就是999 1001 这种情况,直接考虑

再有就是小于10的这种情况,直接减1

最后就是出错的100 1000 10000,直接减1

虽然艰辛,但是过了还是很开心
代码有点重复,可以写个函数优化

然后去看了题解,发现基本

class Solution 
    public String nearestPalindromic(String n) 
        //最近的回文数
        //121513165 最近的回文 121515121 或者 121505121 也就是把中间的1减1,然后回文
        //如果长度是偶数,例如 25385693,最近的回文 25388352 或者比它小的 25377352
        //如果是 12896 那么一个比它小的就是 12821 比它大的就是 12921
        //如果本来是 11199999 那么 11199111 11200211 

        //那么这样看就是找一个比它大的回文数,一个比它小的回文数,比较哪个差的绝对值最小
        //怎么找呢,就是先将前面的一半反转,如果反转的这一半比后面一半大,那么另一个数就是取前面数减1,然后反转
        //如果反转的这一半比后一半小,那么前面加1,再反转得到另一个数

        int l = n.length();
        if(Long.parseLong(n) <= 10)
            return String.valueOf(Long.parseLong(n) - 1);
        if(Long.parseLong(n) == (long)Math.pow(10, l - 1))
            return String.valueOf(Long.parseLong(n) - 1);
        int half = l / 2;
        String front = n.substring(0, half);        //字符串的前半部分
        String behind = n.substring(l - half, l);   //后半部分
        StringBuffer sb = new StringBuffer(front);  //反转前半部分
        String r_f = sb.reverse().toString();   //将前面反转过来
        int r_num = Integer.parseInt(front);    //前半部分的值
        int r_f_num = Integer.parseInt(r_f);    //反转后的值
        int b_num = Integer.parseInt(behind);   //后半部分的值

        int diff = r_f_num - b_num;             //差值就等于前半部分和后半部分的差值
        
        //如果本身就是回文,同时加一减一会影响字符串的长度时,特殊处理
        if(diff == 0)
            long base1 = (long)Math.pow(10, l);
            long base2 = (long)Math.pow(10, l - 1);
            //System.out.println(base2);
            long num = Long.parseLong(n);
            //System.out.println(num - base2);
            if(n.charAt(0) == '1' && num - base2 == 1)
                return String.valueOf(num - 2);
            if(n.charAt(0) == '9' && num - base1 == -1)
                return String.valueOf(num + 2); 
        
        //System.out.println(diff);
        //如果反转以后的大,那么需要减1,否则需要加1
        int add = diff >= 0 ? -1 : 1;
        
        //如果是奇数的话,需要考虑最中间的数
        if(l % 2 == 1)
            front = n.substring(0, half + 1);
            r_num = Integer.parseInt(front);
        
        String first = front + r_f;     //反转前面得到的最终字符串
        
        //将前面加1或者减1
        int temp = r_num;
        r_num += add;
        //System.out.println(r_num);
        
        //前面的字符串最终值
        String f_final = String.valueOf(r_num);
        
        front = f_final;
        //如果是奇数,那么反转的要少一个字符
        if(l % 2 == 1)
            front = f_final.substring(0, f_final.length() - 1);
            r_num = Integer.parseInt(front);    
        

        String b_final = new StringBuffer(front).reverse().toString();
        //System.out.println(b_final);
        //int b_final_num = Integer.parseInt(b_final);
        
        String finall = f_final + b_final;
        //System.out.println(finall);
        long diff_new = Math.abs(Long.parseLong(finall) - Long.parseLong(n));
        diff = Math.abs(diff);
        
        //如果本身是回文,那么加减都需要考虑,前面考虑了减,现在考虑加
        if(diff == 0)
            r_num = temp + 1;
            //前面的字符串最终值
            f_final = String.valueOf(r_num);
            front = f_final;
            //如果是奇数,那么反转的要少一个字符
            if(l % 2 == 1)
                front = f_final.substring(0, f_final.length() - 1);
                r_num = Integer.parseInt(front);    
            
            b_final = new StringBuffer(front).reverse().toString();
            String finall2 = f_final + b_final;
            diff = (int)Math.abs(Long.parseLong(finall2) - Long.parseLong(n));
            if(diff < diff_new)
                return finall2;
            else
                return finall;
        
        if((long)diff > diff_new)
            return finall;
        else if((long)diff < diff_new)
            return first;
        //如果大小都相同的话,那么就是返回较小的值
        if(Long.parseLong(first) < Long.parseLong(finall))
            return first;
        else 
            return finall;
    

258. 各位相加

2022.3.3 每日一题

题目描述

给定一个非负整数 num,反复将各个位上的数字相加,直到结果为一位数。返回这个结果。

示例 1:

输入: num = 38
输出: 2
解释: 各位相加的过程为:
38 --> 3 + 8 --> 11
11 --> 1 + 1 --> 2
由于 2 是一位数,所以返回 2。

示例 1:

输入: num = 0
输出: 0

提示:

0 <= num <= 2^31 - 1

进阶:你可以不使用循环或者递归,在 O(1) 时间复杂度内解决这个问题吗?

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/add-digits
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

递归

class Solution 
    public int addDigits(int num) 
        //想想该怎么弄成O1复杂度
        //先写个递归吧
        if(num < 10)
            return num;
        int res = 0;
        while(num != 0)
            res += num % 10;
            num /= 10;
        
        return addDigits(res);
    

这个O1的方法想不出来,看题解吧
主要是这个式子

class Solution 
    public int addDigits(int num) 
        //想想该怎么弄成O1复杂度
        //先写个递归吧
        //这样,这个数最大是2的31次方-1,也就是2147483647
        //最大的十个数相加就是 1999999999,也就是82
        //那么再看所有返回的个位数,也就是1-9
        //1的情况就是 10
        //2的情况就是 20 11
        //3的情况就是 30 21 12
        //4的情况就是 40 31 22 13
        //以此类推
        //推不下去


        //看了官解的数学解法,我肯定是想不出来的
        //观察那个公式,可以发现,公式中前面的连加式是9的倍数,后面的式子是所有位置的和
        //然后将这个数对 9 取余,那么可以发现只剩下后面那个部分对9取余,也就是所有的位置数字之和对9取余,
        //所以就可以得到一个结论,就是num与所有位置数字之和 对9同余
        //而一直将这个相加的数字对9取余,可以得到最后的个位数与num对9是同余的

        //得到这个结论以后,我们就可以根据num是否是9的倍数分成两种情况,如果不是9的倍数,那么剩下的那个数就是num%9
        //如果是9的倍数,那么对9取余以后得到的0,所以最后结果是9
        
        //最后又做了个操作,就是用num - 1对9取余然后加1,因为对9取余的结果是 0-8 ,而各位相加的结果在 1-9 这个范围内
        //所以用这样的操作,直接得到最终的结果

        //对于剩下的0,0减1对9取余就是-1,然后加1是0,所以不用单独处理
        return (num - 1) % 9 + 1;
        
    

以上是关于LeetCode 6. Z 字形变换 / 564. 寻找最近的回文数 / 258. 各位相加的主要内容,如果未能解决你的问题,请参考以下文章

leetcode 6: Z字形变换

LeetCode 6 Z 字形变换

LeetCode 6. Z 字形变换(中)

LeetCode 6. Z 字形变换(中)

leetcode 6 Z字形变换

[LeetCode] 6. Z 字形变换