LeetCode 005 字符串系列

Posted Al_tair

tags:

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

字符串(三)

大家好!我是小笙!我前段时间忙于学习java后端技术,耽误了算法题的更新,心有惭愧,立下每周的刷题计划不攻自破,现在我再一次踏上刷题之路,坚持下去!加油!


字符串(三)系列题型如下

子序列(392)

392.判断子序列

给定字符串 s 和 t ,判断 s 是否为 t 的子序列。
字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"是"abcde"的一个子序列,而"aec"不是)。
进阶:
如果有大量输入的 S,称作 S1, S2, … , Sk 其中 k >= 10亿,你需要依次检查它们是否为 T
的子序列。在这种情况下,你会怎样改变代码?
示例 1:
输入:s = “abc”, t = “ahbgdc” 输出:true 示例 2:
输入:s = “axc”, t = “ahbgdc” 输出:false
提示:
0 <= s.length <= 100
0 <= t.length <= 10^4
两个字符串都只由小写字符组成。

方法一 下标法(MyCode)

我的想法是根据s的下标移动来解决该问题
首先呢,要想判断s是否是t 的子序列,我觉得肯定是要用到s字符串中的每个字符,所以根据下标移动,一旦满足全部字符按顺序在t中出现,那就返回true
注意 s,t为空的情况!!!

class Solution {
    public boolean isSubsequence(String s, String t) {
        int indexS = 0,n = s.length(), m = t.length();
        if(n == 0) return true;
        if(m == 0) return false;
        
        for(int i=0;i<m;i++){
            if(s.charAt(indexS) == t.charAt(i)){
                indexS++;
            }
            if(indexS == n){
                return true;
            }
        }
        return false;
    }
}
执行用时:1 ms, 在所有 Java 提交中击败了85.17%的用户
内存消耗:36.2 MB, 在所有 Java 提交中击败了77.57%的用户

方法二:动态规划(Other’s Code)

我觉得官方这题用动态规划有点麻烦!
首先让我讲述一下动态规划的概念
动态规划其实质上是通过开辟记录表,记录已求解过的结果,当再次需要求解的时候,可以直接到
那个记录表中去查找,从而避免重复计算子问题来达到降低时间复杂度的效果。实际上是一个空间
换时间
的算法。动态规划,通常可以把指数级的复杂度降低到多项式级别。
形象的描述:拿着字典去找字
思路:
1.先创建“字典”(根据t长度动态考虑该解)
2.拿着“字典”去判断是否有该“字”
3.判断: false / true

class Solution {
    public boolean isSubsequence(String s, String t) {
        int n = s.length(), m = t.length();

        int[][] f = new int[m + 1][26];
        for (int i = 0; i < 26; i++) {
            f[m][i] = m;
        }

        for (int i = m - 1; i >= 0; i--) {
            for (int j = 0; j < 26; j++) {
                if (t.charAt(i) == j + 'a')
                    f[i][j] = i;
                else
                    f[i][j] = f[i + 1][j];
            }
        }
        int add = 0;
        for (int i = 0; i < n; i++) {
            if (f[add][s.charAt(i) - 'a'] == m) {
                return false;
            }
            add = f[add][s.charAt(i) - 'a'] + 1;
        }
        return true;
    }
}

高精度运算(66)

66.加一

给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。 最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。
你可以假设除了整数 0 之外,这个整数不会以零开头。
示例 1: 输入:digits = [1,2,3] 输出:[1,2,4] 解释:输入数组表示数字 123。
示例 2: 输入:digits = [4,3,2,1] 输出:[4,3,2,2] 解释:输入数组表示数字 4321。
示例 3: 输入:digits = [0] 输出:[1]
提示: 1 <= digits.length <= 100 0 <= digits[i] <= 9

方法一 入栈和出栈(MyCode)

class Solution {
    public int[] plusOne(int[] digits) {
        int carry = 1;
        Stack<Integer> stack = new Stack<Integer>();
        for (int i = digits.length - 1; i >= 0 || carry != 0; i--) {
            int sum = carry;
            if (i >= 0) {
                sum += digits[i];
            }
            stack.push(sum % 10);
            carry = sum / 10;
        }
        int[] res = new int[stack.size()];
        for (int i = 0; i < res.length; i++) {
            res[i] = stack.pop();
        }
        return res;
    }
}
执行用时:1 ms, 在所有 Java 提交中击败了100.00%的用户
内存消耗:37 MB, 在所有 Java 提交中击败了20.84%的用户

方法二 绝!(Other’sCode)

该解法真的绝!好妙啊!已经不能让我用其他词语形容该解法了

class Solution {
    public int[] plusOne(int[] digits) {
        for (int i = digits.length - 1; i >= 0; i--) {
            digits[i]++;
            digits[i] = digits[i] % 10;
            if (digits[i] != 0) return digits;
        }
        digits = new int[digits.length + 1]; // 经典
        digits[0] = 1;
        return digits;
    }
}

字符串变换(6)

6. 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

方法一 遍历(MyCode)

如下是我的解题思路:

class Solution {
    public String convert(String s, int numRows) {
        String[] string = new String[numRows];
        Boolean flag = true; // true 向下 false 向上
        int count = 0;
        String sum = "";
         for(int i=0;i<numRows;i++){
            if(string[i]==null){
                string[i]="";
            }
        }
        if(numRows == 1){
            return s;
        }
        for(int i=0;i<s.length();i++){
            // 判断运行方向和行数
            if(flag == true && count<=numRows-1){ // 判断向下运行numRows-1
                count++;
            }else if(flag == true && count>numRows-1){
                count--;
                flag = false;
            }else if(flag == false && count>1){  //判断向上运行numRows-1
                count--;
            }else if(flag == false && count==1){
                count++;
                flag = true;
            }
            string[count-1] += s.charAt(i);
        }
        for(int i=0;i<numRows;i++){
            sum += string[i];
        }
       return sum;
    }
}
执行用时:14 ms, 在所有 Java 提交中击败了22.71%的用户
内存消耗:39.3 MB, 在所有 Java 提交中击败了15.16%的用户

字符串匹配(459)

459. 重复的子字符串

给定一个非空的字符串,判断它是否可以由它的一个子串重复多次构成。给定的字符串只含有小写英文字母,并且长度不超过10000。
示例 1:
输入: “abab”
输出: True
解释: 可由子字符串 “ab” 重复两次构成。
示例 2:
输入: “aba”
输出: False
示例 3:
输入: “abcabcabcabc”
输出: True
解释: 可由子字符串 “abc” 重复四次构成。 (或者子字符串 “abcabc” 重复两次构成。)

方法一分段比较

解题思路:
首先我用depart用来判断分段次数,countflag用来判断是否每一个分段都是相同的
我想最多分 字符串长度n 的段数 while(depart<=n)

class Solution {
    public boolean repeatedSubstringPattern(String s) {
        int n = s.length();
        int depart = 2; // 分几份
        int countflag = 0; // 判断标志位
        while(depart<=n){
            if(n%depart == 0){
                for(int i=0;i<=n-n*2/depart;i+=n/depart){
                    if(s.substring(i,i+n/depart).equals(s.substring(i+n/depart,i+n*2/depart))){
                        countflag++;
                    }
                }
                if(countflag == depart-1){
                    return true;
                }
            }
            countflag = 0;
            depart++;
        }
        return false;
    }
}
执行用时:34 ms, 在所有 Java 提交中击败了45.58%的用户
内存消耗:38.6 MB, 在所有 Java 提交中击败了92.36%的用户

方法二:字符串匹配(Other’s Code)

绝!太强了!无话可说,透彻!

// 	int indexOf(int ch, int fromIndex)
//  返回在此字符串中第一次出现指定字符处的索引,从指定的索引开始搜索。
class Solution {
    public boolean repeatedSubstringPattern(String s) {
        return (s + s).indexOf(s, 1) != s.length();
    }
}


中心拓展法(5)

5. 最长回文子串

给你一个字符串 s,找到 s 中最长的回文子串。
示例 1:
输入:s = “babad”
输出:“bab”
解释:“aba” 同样是符合题意的答案。
示例 2
输入:s = “cbbd”
输出:“bb”
示例 3:
输入:s = “a” 输出:“a” 示例 4:
输入:s = “ac” 输出:“a”
提示:
1 <= s.length <= 1000
s 仅由数字和英文字母(大写和/或小写)组成

方法一暴力解法(MyCode)

首先我想的是写出一个函数判断回文函数palindrome
然后我尝试通过双重循环来从最长字符串的回文字符串开始判断
一旦出现判断是回文函数就返回该字符,反之继续查询
最后还是无解就返回该字符串首字符s.substring(0,1)

class Solution {
    public String longestPalindrome(String s) {
        int n = s.length();
        for(int i=n;i>=2;i--){
            for(int j=0;j+i<=n;j+=1){
                if(palindrome(s.substring(j,j+i))){
                    return s.substring(j,j+i);
                }
            }
        }
        return s.substring(0,1);
    }

    public boolean palindrome(String s){  // 判断回文函数
        int i=0,j=s.length()-1;
        while(i<j){
            if(s.charAt(i) == s.charAt(j)){
                i++;
                j--;
            }else{
                return false;
            }
        }
        return true;
    }
}

方法二:动态规划

动态规划的边界条件:
对于一个子串而言,如果它是回文串,并且长度大于 2,那么将它首尾的两个字母去除之后,它仍然是个回文串。例如对于字符串}“bab” 是回文串,那么“ababa” 一定是回文串,这是因为它的首尾两个字母都是 “a”。

根据这样的思路,我们就可以用动态规划的方法解决本题。我们用 P(i,j)P(i,j) 表示字符串 ss 的第 ii 到 jj 个字母组成的串(下文表示成 s[i:j]s[i:j])是否为回文串:

public class Solution {

    public String longestPalindrome(String s) {
        int len = s.length();
        if (len < 2) {
            return s;
        }

        int maxLen = 1;
        int begin = 0;
        // dp[i][j] 表示 s[i..j] 是否是回文串
        boolean[][] dp = new boolean[len][len];
        // 初始化:所有长度为 1 的子串都是回文串
        for (int i = 0; i < len; i++) {
            dp[i][i] = true;
        }

        char[] charArray = s.toCharArray();
        // 递推开始
        // 先枚举子串长度
        for (int L = 2; L <= len; L++) {
            // 枚举左边界,左边界的上限设置可以宽松一些
            for (int i = 0; i < len; i++) {
                // 由 L 和 i 可以确定右边界,即 j - i + 1 = L 得
                int j = L + i - 1;
                // 如果右边界越界,就可以退出当前循环
                if (j >= len) {
                    break;
                }

                if (charArray[i] != charArray[j]) {
                    dp[i][j] = false;
                } else {
                    if (j - i < 3) {
                        dp[i][j] = true;
                    } else {
                        dp[i][j] = dp[i + 1][j - 1];
                    }
                }

                // 只要 dp[i][L] == true 成立,就表示子串 s[i..L] 是回文,此时记录回文长度和起始位置
                if (dp[i][j] && j - i + 1 > maxLen) {
                    maxLen = j - i + 1;
                    begin = i;
                }
            }
        }
        return s.substring(begin, begin + maxLen);
    }
}

以上是关于LeetCode 005 字符串系列的主要内容,如果未能解决你的问题,请参考以下文章

LeetCode刷题-005最长回文子串

LeetCode刷题--点滴记录005

LeetCode--005--最长回文子串(java)

LeetCode-Algorithms #005 Longest Palindromic Substring, Database #179 Consecutive Numbers

leetcode-005. 最长回文子串--python

leetcode-005. 最长回文子串--python