LeetCode练习day2-字符串相关

LeetCode06 Z字形变换

题目详情

将一个给定字符串 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

代码
class LeetCode06 {
    public static void main(String[] args) {
        System.out.println(new Solution().convert("LEETCODEISHIRING", 4));
        System.out.println(new Solution2().convert("LEETCODEISHIRING", 4));
        System.out.println(new Solution3().convert("LEETCODEISHIRING", 4));
        System.out.println(new Solution4().convert("LEETCODEISHIRING", 4));
    }

    /*
    利用二维矩阵模拟
     */
    static class Solution {
        public String convert(String s, int numRows) {
            int n = s.length(), r = numRows;
            if (r == 1 || r >= n) {
                return s;
            }
            int t = r * 2 - 2;
            int c = (n + t - 1) / t * (r - 1);
            char[][] mat = new char[r][c];
            for (int i = 0, x = 0, y = 0; i < n; ++i) {
                mat[x][y] = s.charAt(i);
                if (i % t < r - 1) {
                    ++x; // 向下移动
                } else {
                    --x;
                    ++y; // 向右上移动
                }
            }
            StringBuffer ans = new StringBuffer();
            for (char[] row : mat) {
                for (char ch : row) {
                    if (ch != 0) {
                        ans.append(ch);
                    }
                }
            }
            return ans.toString();
        }
    }

    /*
    方法二:压缩矩阵空间
     */
    static class Solution2 {
        public String convert(String s, int numRows) {
            int n = s.length(), r = numRows;
            if (r == 1 || r >= n) {
                return s;
            }
            StringBuffer[] mat = new StringBuffer[r];
            for (int i = 0; i < r; ++i) {
                mat[i] = new StringBuffer();
            }
            for (int i = 0, x = 0, t = r * 2 - 2; i < n; ++i) {
                mat[x].append(s.charAt(i));
                if (i % t < r - 1) {
                    ++x;
                } else {
                    --x;
                }
            }
            StringBuffer ans = new StringBuffer();
            for (StringBuffer row : mat) {
                ans.append(row);
            }
            return ans.toString();
        }
    }

    static class Solution3 {
        /*
         * 官方方法3,直接构造
         * 根据方法2可知,一个字符只要直到要放到哪一行即可
         * 假设原字符串字符的下标为idx
         * 周期t = r+(r -2) = 2r-2(解法一已有解释)
         * 可以发现:
         * 对于idx mod t < r的,就会放到第(idx mod t)行,
         * 对于idx mod t > r(小于t)的,就会放到第t - (idx mod t)行
         * 有了这个规律,就可以直接按公式算出要放的行,不用像方法2那样求行了,
         * 只要从前往后依次计算该字符的行,然后直接加到每行的末尾,即可达到效果
         */
        public String convert(String s, int numRows) {
            //特殊情况
            //numRows为1,直接返回s
            if (numRows == 1) {
                return s;
            }
            //s长度 <= numRows,直接返回s
            if (s.length() <= numRows) {
                return s;
            }

            int n = s.length();
            //行数为numRows,简写为r。计算所需的列数,为(n/t向上取整)⋅(r−1)
            // Z 字形变换的周期 t=r+(r−2)=2r−2
            int t = numRows + (numRows - 2);

            //定义数组,每行用StringBuilder
            StringBuilder[] matrix = new StringBuilder[numRows];
            for (int i = 0; i < matrix.length; i++) {
                matrix[i] = new StringBuilder();
            }

            //填入每个字符
            for (int i = 0; i < n; i++) {
                //计算要放的行
                int targetRow;
                int mod = i % t;
                if (mod < numRows) {
                    //对于idx mod t < r的,就会放到第(idx mod t)行
                    targetRow = mod;
                } else {
                    //对于idx mod t >= r(小于t)的,就会放到第t - (idx mod t)行
                    targetRow = t - mod;
                }
                //放到对应行
                matrix[targetRow].append(s.charAt(i));
            }

            //输出结果字符串
            StringBuilder result = new StringBuilder();
            for (int i = 0; i < numRows; i++) {
                result.append(matrix[i]);
            }
            return result.toString();
        }
    }

    /*
    flag使用
     */
    static class Solution4 {
        public String convert(String s, int numRows) {
            if (numRows < 2) return s;
            List rows = new ArrayList();
            for (int i = 0; i < numRows; i++) rows.add(new StringBuilder());
            int i = 0, flag = -1;
            for (char c : s.toCharArray()) {
                rows.get(i).append(c);
                if (i == 0 || i == numRows - 1) flag = -flag;
                i += flag;
            }
            StringBuilder res = new StringBuilder();
            for (StringBuilder row : rows) res.append(row);
            return res.toString();
        }
    }
}

LeetCode07 整数反转

题目详情

给你一个 32 位的有符号整数 x ,返回将 x 中的数字部分反转后的结果。
如果反转后整数超过 32 位的有符号整数的范围 [−231, 231 − 1] ,就返回 0。
假设环境不允许存储 64 位整数(有符号或无符号)。

示例 1:

输入:x = 123
输出:321
示例 2:

输入:x = -123
输出:-321
示例 3:

输入:x = 120
输出:21
示例 4:

输入:x = 0
输出:0

提示:

-231 <= x <= 231 - 1

代码
public class LeetCode07 {
    public static void main(String[] args) {
        System.out.println(new Solution().reverse(619));
        System.out.println(new Solution2().reverse(619));
    }

    /*
    方法:弹出和推入数字 & 溢出前进行检查
    思路

    我们可以一次构建反转整数的一位数字。在这样做的时候,我们可以预先检查向原整数附加另一位数字是否会导致溢出。

    算法

    反转整数的方法可以与反转字符串进行类比。

    我们想重复“弹出” x 的最后一位数字,并将它“推入”到 rev 的后面。最后,rev 将与 x 相反。

    要在没有辅助堆栈 / 数组的帮助下 “弹出” 和 “推入” 数字,我们可以使用数学方法。

    //pop operation:
    pop = x % 10;
    x /= 10;

    //push operation:
    temp = rev * 10 + pop;
    rev = temp;

    但是,这种方法很危险,因为当 temp=rev⋅10+pop 时会导致溢出。

    幸运的是,事先检查这个语句是否会导致溢出很容易。

     */
    static class Solution {
        public int reverse(int x) {
            int rev = 0;
            while (x != 0) {
                int pop = x % 10;//出栈
                x /= 10;//指下一个

                if (rev > Integer.MAX_VALUE / 10 || (rev == Integer.MAX_VALUE / 10 && pop > 7))
                    return 0;
                if (rev < Integer.MIN_VALUE / 10 || (rev == Integer.MIN_VALUE / 10 && pop < -8))
                    return 0;

                rev = rev * 10 + pop;//入栈
            }
            return rev;
        }
    }

    /*
    数学
     */
    static class Solution2 {
        public int reverse(int x) {
            int rev = 0;
            while (x != 0) {
                if (rev < Integer.MIN_VALUE / 10 || rev > Integer.MAX_VALUE / 10) {
                    return 0;
                }
                int digit = x % 10;
                x /= 10;
                rev = rev * 10 + digit;
            }
            return rev;
        }
    }
}


LeetCode08 字符串转换整数(atoi)

题目详情

字符串转换整数(atoi)

请你来实现一个atoi函数,使其能将字符串转换成整数。

首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。接下来的转化规则如下:

如果第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字字符组合起来,形成一个有符号整数。
假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成一个整数。
该字符串在有效的整数部分之后也可能会存在多余的字符,那么这些字符可以被忽略,它们对函数不应该造成影响。
注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换,即无法进行有效转换。

在任何情况下,若函数不能进行有效的转换时,请返回 0 。

提示:

本题中的空白字符只包括空格字符 ' ' 。
假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为[−231, 231− 1]。如果数值超过这个范围,请返回 INT_MAX (231− 1) 或INT_MIN (−231) 。

示例1:

输入: "42"
输出: 42
示例2:

输入: " -42"
输出: -42
解释: 第一个非空白字符为 '-', 它是一个负号。
我们尽可能将负号与后面所有连续出现的数字组合起来,最后得到 -42 。
示例3:

输入: "4193 with words"
输出: 4193
解释: 转换截止于数字 '3' ,因为它的下一个字符不为数字。
示例4:

输入: "words and 987"
输出: 0
解释: 第一个非空字符是 'w', 但它不是数字或正、负号。
因此无法执行有效的转换。
示例5:

输入: "-91283472332"
输出: -2147483648
解释: 数字 "-91283472332" 超过 32 位有符号整数范围。
因此返回 INT_MIN (−231) 。

代码
class LeetCode08 {
    public static void main(String[] args) {
        System.out.println(new Solution().myAtoi("  4193 with words"));
        System.out.println(new Solution2().myAtoi("  4193 with words"));
        System.out.println(new Solution3().myAtoi("  4193 with words"));
    }

    /*
    这是确定有限状态机(deterministic finite automaton, DFA)。
    其实这题已经不算是容易写“出臃肿代码”的了。
    考虑清楚边界(主要是溢出处理)和输入种类(+, -, 0-9以及其他),大概在20行内完成代码不难。
    说实话LC官方题解里很少见给出标准DFA解法的,点个赞。
    给两个更加需要DFA的题目吧:utf-8-validation ( 附dfa解法 ) 以及 valid-number。
    顺便贴一下普通解法(那种臃肿的、易错的、可能会被test cases虐到骂娘的;但如果骂娘了,本质还是基本功不扎实)。
     */
    static class Solution {
        public int myAtoi(String str) {
            str = str.trim();
            if (str.length() == 0) return 0;
            if (!Character.isDigit(str.charAt(0))
                    && str.charAt(0) != '-' && str.charAt(0) != '+')
                return 0;
            long ans = 0L;
            boolean neg = str.charAt(0) == '-';
            int i = !Character.isDigit(str.charAt(0)) ? 1 : 0;
            while (i < str.length() && Character.isDigit(str.charAt(i))) {
                ans = ans * 10 + (str.charAt(i++) - '0');
                if (!neg && ans > Integer.MAX_VALUE) {
                    ans = Integer.MAX_VALUE;
                    break;
                }
                if (neg && ans > 1L + Integer.MAX_VALUE) {
                    ans = 1L + Integer.MAX_VALUE;
                    break;
                }
            }
            return neg ? (int) -ans : (int) ans;
        }
    }

    /*
    自动机
     */
    static class Solution2 {
        static class Automaton {
            final String START = "start";
            final String SIGNED = "signed";
            final String IN_NUM = "in_number";
            final String END = "end";
            String state = START;
            Map map;
            public int sign = 1;
            public long ans = 0;

            public Automaton() {
                map = new HashMap<>();
                map.put(START, new String[]{START, SIGNED, IN_NUM, END});
                map.put(SIGNED, new String[]{END, END, IN_NUM, END});
                map.put(IN_NUM, new String[]{END, END, IN_NUM, END});
                map.put(END, new String[]{END, END, END, END});
            }

            public int get_col(char c) {
                if (c == ' ') return 0;
                if (c == '+' || c == '-') return 1;
                if (c >= '0' && c <= '9') return 2;
                return 3;
            }

            public void get(char c) {
                state = map.get(state)[get_col(c)];
                if (state.equals(IN_NUM)) {
                    ans = ans * 10 + c - '0';
                    if (sign == 1) {
                        ans = Math.min(ans, Integer.MAX_VALUE);
                    } else {
                        // -(long)Integer.MIN_VALUE,这个操作有点东西,不然越界
                        ans = Math.min(ans, -(long) Integer.MIN_VALUE);
                    }
                } else if (state.equals(SIGNED))
                    sign = c == '+' ? 1 : -1;
            }
        }

        public int myAtoi(String str) {
            Automaton automaton = new Automaton();
            char[] c = str.toCharArray();
            for (char ch : c) {
                automaton.get(ch);
            }
            return automaton.sign * ((int) automaton.ans);
        }
    }

    /*
    直接写
     */
    static class Solution3 {
        public int myAtoi(String s) {
            int i = 0;
            int len = s.length();
            int sign = 1;
            int res = 0;
            while (i < len && s.charAt(i) == ' ') { //如果字符串前导位置为空格,循环到有数据的那一个位置
                i++;
            }
            int start = i;  //记录一下当前之后所有数据开始的位置
            for (; i < len; i++) {
                int c = s.charAt(i);
                if (i == start && c == '+') {   //判断是否是+,并且+要在初始位置
                    sign = 1;
                } else if (i == start && c == '-') {    //判断是-
                    sign = -1;
                } else if (Character.isDigit(c)) {  //判断是数字
                    int num = c - '0';
                    //如果是数字,其他不用考虑,只需要考虑两种超限的情况,这里不细说,具体去"https://leetcode-cn.com/problems/reverse-integer/"看
                    if (res > Integer.MAX_VALUE / 10 || (res == Integer.MAX_VALUE / 10 && num > Integer.MAX_VALUE % 10)) {
                        return Integer.MAX_VALUE;
                    } else if (res < Integer.MIN_VALUE / 10 || (res == Integer.MIN_VALUE / 10 && -num < Integer.MIN_VALUE % 10)) {
                        return Integer.MIN_VALUE;
                    }
                    res = res * 10 + sign * num;
                } else {    //如果有一次循环既不是数字,又不是'+'和'-',那么立即退出循环,并返回当前res中已经储存的值
                    break;
                }
            }
            return res;
        }
    }
}

LeetCode14 最长公共前缀

题目详情

编写一个函数来查找字符串数组中的最长公共前缀。
如果不存在公共前缀,返回空字符串 ""。

示例 1:

输入:strs = ["flower","flow","flight"]
输出:"fl"
示例 2:

输入:strs = ["dog","racecar","car"]
输出:""
解释:输入不存在公共前缀。

提示:

1 <= strs.length <= 200
0 <= strs[i].length <= 200
strs[i] 仅由小写英文字母组成

代码
public class LeetCode14 {
    public static void main(String[] args) {
        System.out.print(new Solution().longestCommonPrefix(new String[]{"flowsad", "flosad", "flisad"}));
    }

    /*
    方法一是横向扫描,依次遍历每个字符串,更新最长公共前缀。另一种方法是纵向扫描。
    纵向扫描时,从前往后遍历所有字符串的每一列,比较相同列上的字符是否相同,如果相同则继续对下一列进行比较,如果不相同则当前列不再属于公共前缀,当前列之前的部分为最长公共前缀。
     */
    static class Solution {
        public String longestCommonPrefix(String[] strs) {
            if (strs == null || strs.length == 0) return "";
            int i = 0;
            StringBuilder res = new StringBuilder();
            while (i < strs[0].length()) {
                String pre = strs[0];
                for (int j = 1; j < strs.length; j++) {
                    if (strs[j].length() < i + 1 || pre.charAt(i) != strs[j].charAt(i)) {
                        return res.toString();
                    }
                    pre = strs[j];
                }
                res.append(pre.charAt(i));
                i++;
            }
            return res.toString();
        }
    }
}

LeetCode30 串联所有单词的子串

题目详情

给定一个字符串 s 和一些 长度相同 的单词 words 。找出 s 中恰好可以由 words 中所有单词串联形成的子串的起始位置。
注意子串要与 words 中的单词完全匹配,中间不能有其他字符 ,但不需要考虑 words 中单词串联的顺序。

示例 1:

输入:s = "barfoothefoobarman", words = ["foo","bar"]
输出:[0,9]
解释:
从索引 0 和 9 开始的子串分别是 "barfoo" 和 "foobar" 。
输出的顺序不重要, [9,0] 也是有效答案。
示例 2:

输入:s = "wordgoodgoodgoodbestword", words = ["word","good","best","word"]
输出:[]
示例 3:

输入:s = "barfoofoobarthefoobarman", words = ["bar","foo","the"]
输出:[6,9,12]

提示:

1 <= s.length <= 104
s 由小写英文字母组成
1 <= words.length <= 5000
1 <= words[i].length <= 30
words[i] 由小写英文字母组成

代码
class LeetCode30 {
    public static void main(String[] args) {
        System.out.println(new Solution().findSubstring("barfoothefoobarman", new String[]{"foo", "bar"}));
        System.out.println(new Solution2().findSubstring("barfoothefoobarman", new String[]{"foo", "bar"}));
    }

    /*
    思路一:

因为单词长度固定的,我们可以计算出截取字符串的单词个数是否和 words 里相等,所以我们可以借用哈希表。

一个是哈希表是 words,一个哈希表是截取的字符串,比较两个哈希是否相等!

因为遍历和比较都是线性的,所以时间复杂度:O(n^2)O(n
2
 )

上面思路每次都要反复遍历 s;下面介绍滑动窗口。

思路二:

滑动窗口!

我们一直在 s 维护着所有单词长度总和的一个长度队列!

时间复杂度:O(n)O(n)

还可以再优化,只是加一些判断,详细看代码吧!

     */
    static class Solution {
        public List findSubstring(String s, String[] words) {
            List res = new ArrayList<>();
            if (s == null || s.length() == 0 || words == null || words.length == 0) return res;
            HashMap map = new HashMap<>();
            int one_word = words[0].length();
            int word_num = words.length;
            int all_len = one_word * word_num;
            for (String word : words) {
                map.put(word, map.getOrDefault(word, 0) + 1);
            }
            for (int i = 0; i < s.length() - all_len + 1; i++) {
                String tmp = s.substring(i, i + all_len);
                HashMap tmp_map = new HashMap<>();
                for (int j = 0; j < all_len; j += one_word) {
                    String w = tmp.substring(j, j + one_word);
                    tmp_map.put(w, tmp_map.getOrDefault(w, 0) + 1);
                }
                if (map.equals(tmp_map)) res.add(i);
            }
            return res;
        }
    }

    static class Solution2 {
        public List findSubstring(String s, String[] words) {
            List res = new ArrayList<>();
            if (s == null || s.length() == 0 || words == null || words.length == 0) return res;
            HashMap map = new HashMap<>();
            int one_word = words[0].length();
            int word_num = words.length;
            int all_len = one_word * word_num;
            for (String word : words) {
                map.put(word, map.getOrDefault(word, 0) + 1);
            }
            for (int i = 0; i < one_word; i++) {
                int left = i, right = i, count = 0;
                HashMap tmp_map = new HashMap<>();
                while (right + one_word <= s.length()) {
                    String w = s.substring(right, right + one_word);
                    right += one_word;
                    if (!map.containsKey(w)) {
                        count = 0;
                        left = right;
                        tmp_map.clear();
                    } else {
                        tmp_map.put(w, tmp_map.getOrDefault(w, 0) + 1);
                        count++;
                        while (tmp_map.getOrDefault(w, 0) > map.getOrDefault(w, 0)) {
                            String t_w = s.substring(left, left + one_word);
                            count--;
                            tmp_map.put(t_w, tmp_map.getOrDefault(t_w, 0) - 1);
                            left += one_word;
                        }
                        if (count == word_num) res.add(left);
                    }
                }
            }
            return res;
        }
    }
}

LeetCode131 分割回文串

题目详情

给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是 回文串 。返回 s 所有可能的分割方案。
回文串 是正着读和反着读都一样的字符串。

示例 1:

输入:s = "aab"
输出:[["a","a","b"],["aa","b"]]
示例 2:

输入:s = "a"
输出:[["a"]]

提示:

1 <= s.length <= 16
s 仅由小写英文字母组成

代码
class LeetCode131 {
    public static void main(String[] args) {
        //todo 回溯-backtrack
        System.out.println(new Solution1().partition("aab"));
        System.out.println(new Solution2().partition("aab"));
    }

    static class Solution1 {
        public List> partition(String s) {
            int len = s.length();
            List> res = new ArrayList<>();
            if (len == 0) {
                return res;
            }
            // Stack 这个类 Java 的文档里推荐写成 Deque stack = new ArrayDeque();
            // 注意:只使用 stack 相关的接口
            Deque stack = new ArrayDeque<>();
            backtracking(s, 0, len, stack, res);
            return res;
        }

        /*
         * @param s
         * @param start 起始字符的索引
         * @param len   字符串 s 的长度,可以设置为全局变量
         * @param path  记录从根结点到叶子结点的路径
         * @param res   记录所有的结果
         */
        private void backtracking(String s, int start, int len, Deque path, List> res) {
            if (start == len) {
                res.add(new ArrayList<>(path));
                return;
            }

            for (int i = start; i < len; i++) {

                // 因为截取字符串是消耗性能的,因此,采用传子串索引的方式判断一个子串是否是回文子串
                // 不是的话,剪枝
                if (!checkPalindrome(s, start, i)) {
                    continue;
                }

                path.addLast(s.substring(start, i + 1));
                backtracking(s, i + 1, len, path, res);
                path.removeLast();
            }
        }

        /*
         * 这一步的时间复杂度是 O(N),因此,可以采用动态规划先把回文子串的结果记录在一个表格里
         *
         * @param str
         * @param left  子串的左边界,可以取到
         * @param right 子串的右边界,可以取到
         * @return
         */
        private boolean checkPalindrome(String str, int left, int right) {
            // 严格小于即可
            while (left < right) {
                if (str.charAt(left) != str.charAt(right)) {
                    return false;
                }
                left++;
                right--;
            }
            return true;
        }
    }

    static class Solution2 {
        public List> partition(String s) {
            int len = s.length();
            List> res = new ArrayList<>();
            if (len == 0) {
                return res;
            }

            // 预处理
            // 状态:dp[i][j] 表示 s[i][j] 是否是回文
            boolean[][] dp = new boolean[len][len];
            // 状态转移方程:在 s[i] == s[j] 的时候,dp[i][j] 参考 dp[i + 1][j - 1]
            for (int right = 0; right < len; right++) {
                // 注意:left <= right 取等号表示 1 个字符的时候也需要判断
                for (int left = 0; left <= right; left++) {
                    if (s.charAt(left) == s.charAt(right) && (right - left <= 2 || dp[left + 1][right - 1])) {
                        dp[left][right] = true;
                    }
                }
            }

            Deque stack = new ArrayDeque<>();
            backtracking(s, 0, len, dp, stack, res);
            return res;
        }

        private void backtracking(String s,
                                  int start,
                                  int len,
                                  boolean[][] dp,
                                  Deque path,
                                  List> res) {
            if (start == len) {
                res.add(new ArrayList<>(path));
                return;
            }

            for (int i = start; i < len; i++) {
                // 剪枝
                if (!dp[start][i]) {
                    continue;
                }
                path.addLast(s.substring(start, i + 1));
                backtracking(s, i + 1, len, dp, path, res);
                path.removeLast();
            }
        }
    }
}


LeetCode165 比较版本号

题目详情

给你两个版本号 version1 和 version2 ,请你比较它们。
版本号由一个或多个修订号组成,各修订号由一个 '.' 连接。每个修订号由 多位数字 组成,可能包含 前导零 。每个版本号至少包含一个字符。修订号从左到右编号,下标从 0 开始,最左边的修订号下标为 0 ,下一个修订号下标为 1 ,以此类推。例如,2.5.33 和 0.1 都是有效的版本号。
比较版本号时,请按从左到右的顺序依次比较它们的修订号。比较修订号时,只需比较 忽略任何前导零后的整数值 。也就是说,修订号 1 和修订号 001 相等 。如果版本号没有指定某个下标处的修订号,则该修订号视为 0 。例如,版本 1.0 小于版本 1.1 ,因为它们下标为 0 的修订号相同,而下标为 1 的修订号分别为 0 和 1 ,0 < 1 。

返回规则如下:
如果 version1 > version2 返回 1,
如果 version1 < version2 返回 -1,
除此之外返回 0。

示例 1:

输入:version1 = "1.01", version2 = "1.001"
输出:0
解释:忽略前导零,"01" 和 "001" 都表示相同的整数 "1"
示例 2:

输入:version1 = "1.0", version2 = "1.0.0"
输出:0
解释:version1 没有指定下标为 2 的修订号,即视为 "0"
示例 3:

输入:version1 = "0.1", version2 = "1.1"
输出:-1
解释:version1 中下标为 0 的修订号是 "0",version2 中下标为 0 的修订号是 "1" 。0 < 1,所以 version1 < version2

提示:
1 <= version1.length, version2.length <= 500
version1 和 version2 仅包含数字和 '.'
version1 和 version2 都是 有效版本号
version1 和 version2 的所有修订号都可以存储在 32 位整数 中

代码
class LeetCode165 {
    public static void main(String[] args) {
        System.out.println(new Solution().compareVersion("1.01", "1.001"));
        System.out.println(new Solution2().compareVersion("1.01", "1.001"));
    }

    //方法一:分割+解析,两次遍历,线性空间
    static class Solution {
        public int compareVersion(String version1, String version2) {
            String[] nums1 = version1.split("\\.");
            String[] nums2 = version2.split("\\.");
            int n1 = nums1.length, n2 = nums2.length;
            // 比较块
            int i1, i2;
            for (int i = 0; i < Math.max(n1, n2); ++i) {
                i1 = i < n1 ? Integer.parseInt(nums1[i]) : 0;
                i2 = i < n2 ? Integer.parseInt(nums2[i]) : 0;
                if (i1 != i2) {
                    return i1 > i2 ? 1 : -1;
                }
            }
            // 版本相等
            return 0;
        }
    }

    //方法二:双指针,一次遍历,常数空间
    static class Solution2 {
        public Pair getNextChunk(String version, int n, int p) {
            // 如果指针指向字符串末尾,返回0
            if (p > n - 1) {
                return new Pair(0, p);
            }
            // 找到块的结尾
            int i, pEnd = p;
            while (pEnd < n && version.charAt(pEnd) != '.') {
                ++pEnd;
            }
            // 检索块
            if (pEnd != n - 1) {
                i = Integer.parseInt(version.substring(p, pEnd));
            } else {
                i = Integer.parseInt(version.substring(p, n));
            }
            // 找到下一个块的开始
            p = pEnd + 1;
            return new Pair(i, p);
        }

        public int compareVersion(String version1, String version2) {
            int p1 = 0, p2 = 0;
            int n1 = version1.length(), n2 = version2.length();
            // 比较版本
            int i1, i2;
            Pair pair;
            while (p1 < n1 || p2 < n2) {
                pair = getNextChunk(version1, n1, p1);
                i1 = pair.first;
                p1 = pair.second;

                pair = getNextChunk(version2, n2, p2);
                i2 = pair.first;
                p2 = pair.second;
                if (i1 != i2) {
                    return i1 > i2 ? 1 : -1;
                }
            }
            // 版本相等
            return 0;
        }
    }
}

LeetCode344 反转字符串

题目详情

编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。
不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。

示例 1:

输入:s = ["h","e","l","l","o"]
输出:["o","l","l","e","h"]
示例 2:

输入:s = ["H","a","n","n","a","h"]
输出:["h","a","n","n","a","H"]

提示:

1 <= s.length <= 105
s[i] 都是 ASCII 码表中的可打印字符

代码
public class LeetCode344 {
    public static void main(String[] args) {
        //todo 双指针
        char[] s = {'h', 'e', 'l', 'l', 'o'};
        new Solution().reverseString(s);
        System.out.print(s);
    }

    //反向双指针
    static class Solution {
        public void reverseString(char[] s) {
            int left = 0, right = s.length - 1;
            while (left < right) {
                char tmp = s[left];
                s[left++] = s[right];
                s[right--] = tmp;
            }
        }
    }
}

你可能感兴趣的:(LeetCode练习day2-字符串相关)