字符串相关题目

3. 无重复字符的最长子串

在这里插入图片描述

public static int lengthOfLongestSubstring(String s) {
    int n = s.length(), ans = 0;
    Map<Character, Integer> map = new HashMap<>();
    for (int end = 0, start = 0; end < n; end++) {
        char letter = s.charAt(end);
        // 如果该字符已经被加入到了map中,更新start的位置
        if (map.containsKey(letter)) {
            start = Math.max(map.get(letter), start);
        }
        // 遍历一次算一次最大长度
        ans = Math.max(ans, end - start + 1);
        // 将遍历到字符存入map中,key为字母,value为字母下标 + 1
        map.put(s.charAt(end), end + 1);
    }
    return ans;
}

5. 最长回文子串

public class Solution {
    public static void main(String[] args) {
        String str = "ababaabc";
        System.out.println(longestPalindrome(str));
    }

    public static String longestPalindrome(String s) {
        if (s == null || s.length() < 1) return "";
        int start = 0, end = 0;
        for (int i = 0; i < s.length(); i++) {
            // 中心点是一个字符的时候:dbabc a
            int len1 = expandAroundCenter(s, i, i);
            // 中心点是两个字符的时候:cabbad bb
            int len2 = expandAroundCenter(s, i, i + 1);
            // 回文子串的长度
            int len = Math.max(len1, len2);
            // 本次循环中回文子串的长度大于上次回文串的长度,更新start和end的值
            if (len > end - start) {
                //回文子串的起始下标
                start = i - (len - 1) / 2;
                //回文子串的结束下标
                end = i + len / 2;
            }
        }
        // substring()方法返回的子字符串,左闭右开
        return s.substring(start, end + 1);
    }

    /**
     * 求扩展区的长度 左右闭
     * left == right 的时候以一个字符串为中心开始比较
     * left < right  的时候以两个字符串为中心开始比较(先比较这两个字符串是否相同)
     * @param s
     * @param left   起始下标
     * @param right  结束下标
     * @return
     */
    private static int expandAroundCenter(String s, int left, int right) {
        int L = left, R = right;
        while (L >= 0 && R < s.length() && s.charAt(L) == s.charAt(R)) {
            L--;
            R++;
        }
        return R - L - 1;
    }
}

6. Z 字形变换

class Solution {
    public static void main(String[] args) {
        String str = "LEETCOD";
        String convert = convert(str, 3);
        System.out.println(convert);
    }
    
    public static String convert(String s, int numRows) {
        if (numRows < 2) {
            return s;
        }
        List<StringBuilder> rows = new ArrayList<>();
        // 为每一行创建一个StringBuilder对象来连接字符
        for (int i = 0; i < numRows; i++) {
            rows.add(new StringBuilder());
        }
        // i用来控制添加字母到哪一行
        int i = 0;
        // flag表示 从上到下 还是从下到上的添加过程
        int flag = -1;
        for(char c : s.toCharArray()) {
            rows.get(i).append(c);
            // 如果i = 0, 则从上到下的行顺序添加,此时flag = 1
            // 如果i = numRows - 1,则从下到上的行顺序添加,flag = -1
            if (i == 0 || i == numRows - 1) {
                flag = - flag;
            }
            // 更新行号
            i += flag;
        }
        StringBuilder res = new StringBuilder();
        for (StringBuilder row : rows) {
            res.append(row);
        }
        return res.toString();
    }
}

14. 最长公共前缀

class Solution {
    public static void main(String[] args) {
        String[] strs = new String[]{"flower", "flow", "flight"};
        System.out.println(longestCommonPrefix(strs));
    }

    public static String longestCommonPrefix(String[] strs) {
        if (strs.length == 0 || strs[0].length() == 0) {
            return "";
        }
        //假设第一个为最长的,用第一个一次去和后面的比
        String ans = strs[0];
        //从第二个字符串开始比较
        for (int i = 1; i < strs.length; i++) {
            int j = 0;
            //比较字符串中的每个字符
            for (; j < Math.min(ans.length(), strs[i].length()); j++) {
                //如果有不相等的,ans保留相等的部分
                if (ans.charAt(j) != strs[i].charAt(j)) {
                    if (j == 0) {
                        return "";
                    }
                    break;
                }
            }
            ans = ans.substring(0, j);
        }
        return ans;
    }
}

17. 电话号码的字母组合

/**
 * 给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。
 * 输入:"23"
 * 输出:["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"]
 */
class Solution {
    private static Map<String, String> phone = new HashMap<String, String>() {{
        put("2", "abc");
        put("3", "def");
        put("4", "ghi");
        put("5", "jkl");
        put("6", "mno");
        put("7", "pqrs");
        put("8", "tuv");
        put("9", "wxyz");
    }};

    private static List<String> output = new ArrayList<>();

    public static void main(String[] args) {
        Solution solution = new Solution();
        String digits = "23";
        solution.letterCombinations(digits);
        System.out.println(Arrays.asList(output).toString());
    }

    public List<String> letterCombinations(String digits) {
        if (digits.length() != 0)
            backtrack("", digits);
        return output;
    }

    public void backtrack(String combination, String next_digits) {
        // 递归退出条件,如果数字字符串为空,将已经组合好的字符串加入到结果,然后返回
        if (next_digits.length() == 0) {
            output.add(combination);
        }
        // 如果数字字符串不为空
        else {
            // 从数字字符串前面截取一个数字 (2)
            String digit = next_digits.substring(0, 1);
            // 获取数字对应的字母字符串 (2 - abc)
            String letters = phone.get(digit);
            // 遍历字符串
            for (int i = 0; i < letters.length(); i++) {
                // 获取字符串中的每一个字母 (a)
                String letter = phone.get(digit).substring(i, i + 1);
                // 递归进行字母的组合 ("" + a, 3)
                backtrack(combination + letter, next_digits.substring(1));
            }
        }
    }
}

20. 有效的括号

在这里插入图片描述
方法一

class Solution {
    public boolean isValid(String s) {
        if (s == null || "".equals(s)) {
            return true;
        }
        //用栈保存 (,[,{
        Stack<Character> stack = new Stack<>();
        //当遍历到 )时候就会去map中找对应的value,也就是(
        //再用这个value和stack弹出的元素比较,如果相等则匹配上,不等则返回false
        HashMap<Character, Character> map = new HashMap<>();
        // key 是结束括号,value是开始括号
        map.put(')', '(');
        map.put(']', '[');
        map.put('}', '{');
        for (int i = 0; i < s.length(); i++) {
            // 获取当前字符
            char c = s.charAt(i);
            //1、当前是开始括号 ( [ { 就放入栈中
            if (!map.containsKey(c)) {
                stack.add(c);
            } else {
                //2、当前是结束括号 ) ] } 就从栈中拿出一个
                //如果栈为空,则还没有遍历到左括号(一上来就是右括号),不完整
                if (stack.size() == 0) {
                    return false;
                }
                //如果栈中有左括号,就弹出一个
                Character tmp = stack.pop();
                //假设当前遍历到的字符是:],那么从map中取到的value就是:[
                //如果栈顶的元素是 (,则不匹配返回false,否则继续
                if (map.get(c) != tmp) {
                    return false;
                }
            }
        }
        //返回的时候还要判断栈是否为空
        //如果输入的字符串是 (((,那么最后栈就不为空
        return stack.isEmpty();
    }
}

在这里插入图片描述
方法二

    /**
     * 只用栈实现
     * 思路:碰到开始括号,栈中压入对应的结束括号
     *      碰到结束括号,从栈中弹出一个看是否和当前括号一样
     * @param s
     * @return
     */
    public boolean isValid2(String s) {
        if (s.isEmpty())
            return true;
        Stack<Character> stack = new Stack<>();
        for (char c : s.toCharArray()) {
            if (c == '(')
                stack.push(')');
            else if (c == '{')
                stack.push('}');
            else if (c == '[')
                stack.push(']');
            else if (stack.empty() || c != stack.pop())
                return false;
        }
        return stack.isEmpty();
    }

22. 括号生成

在这里插入图片描述
在这里插入图片描述

//https://leetcode-cn.com/problems/generate-parentheses/solution/
public class Solution {

    public List<String> generateParenthesis(int n) {
        List<String> res = new ArrayList<>();
        // 特判
        if (n == 0) {
            return res;
        }
        // 执行深度优先遍历,搜索可能的结果
        dfs("", n, n, res);
        return res;
    }

    /**
     * @param curStr 当前递归得到的结果
     * @param left   左括号还有几个可以使用
     * @param right  右括号还有几个可以使用
     * @param res    结果集
     */
    private void dfs(String curStr, int left, int right, List<String> res) {
        // 因为每一次尝试,都使用新的字符串变量,所以无需回溯
        // 在递归终止的时候,直接把它添加到结果集即可,注意与「力扣」第 46 题、第 39 题区分
        if (left == 0 && right == 0) {
            res.add(curStr);
            return;
        }

        // 剪枝(如图,左括号可以使用的个数严格大于右括号可以使用的个数,才剪枝,注意这个细节)
        if (left > right) {
            return;
        }

        if (left > 0) {
            dfs(curStr + "(", left - 1, right, res);
        }

        if (right > 0) {
            dfs(curStr + ")", left, right - 1, res);
        }
    }
}

43. 字符串相乘

方法一

class Solution {
    public static void main(String[] args) {
        String num1 = "123";
        String num2 = "456";
        System.out.println(multiply(num1, num2));
    }
    public static String multiply(String num1, String num2) {
        if (num1.equals("0") || num2.equals("0")) {
            return "0";
        }
        // 保存计算结果
        String res = "0";

        // num2 每一位 与 num1 相乘,字符串从尾到头依次截取
        for (int i = num2.length() - 1; i >= 0; i--) {
            int carry = 0;
            // 保存 num2 第i位数字与 num1 相乘的结果
            StringBuilder temp = new StringBuilder();
            // num2 中除了个位外,其余位的结果补0,十位补一个0,依次类推
            for (int j = 0; j < num2.length() - 1 - i; j++) {
                temp.append(0);
            }
            // 截取num2的每一位:个位 -> 十位 -> 百位。。。
            // 并将char类型转为int类型
            int n2 = num2.charAt(i) - '0';
            // num2 的第 i 位数字 n2 与 num1 相乘
            for (int j = num1.length() - 1; j >= 0 || carry != 0; j--) {
                // num1 的最高位与 n2 相乘之后可能有进位,此时 j=-1,carry为进位值
                int n1 = j < 0 ? 0 : num1.charAt(j) - '0';
                // n2 与 num1 的每一位乘之后的个位
                int product = (n1 * n2 + carry) % 10;
                temp.append(product);
                // n2 与 num1 的每一位乘之后的进位
                carry = (n1 * n2 + carry) / 10;
            }
            // 将当前结果与新计算的结果求和作为新的结果
            res = addStrings(res, temp.reverse().toString());
        }
        return res;
    }

    /**
     * 对两个字符串数字进行相加,返回字符串形式的和
     */
    public static String addStrings(String num1, String num2) {
        StringBuilder builder = new StringBuilder();
        // 相加大于10的时候,进位的值
        int carry = 0;
        for (int i = num1.length() - 1, j = num2.length() - 1;
             i >= 0 || j >= 0 || carry != 0;
             i--, j--) {
            int x = i < 0 ? 0 : num1.charAt(i) - '0';
            int y = j < 0 ? 0 : num2.charAt(j) - '0';
            // 相加大于10时,sum值为个位
            int sum = (x + y + carry) % 10;
            // 添加到和字符串
            builder.append(sum);
            // 相加大于10时,计算进位
            carry = (x + y + carry) / 10;
        }
        return builder.reverse().toString();
    }
}

在这里插入图片描述
方法二

public static String multiply(String num1, String num2) {
    if (num1.equals("0") || num2.equals("0")) {
        return "0";
    }
    // 乘积的最大总位数,存放每次相乘之后每位的数
    int[] res = new int[num1.length() + num2.length()];
    for (int i = num1.length() - 1; i >= 0; i--) {
        // 从个位开始截取 num1中的每个数
        int n1 = num1.charAt(i) - '0';
        for (int j = num2.length() - 1; j >= 0; j--) {
            // 从个位开始截取 num1中的每个数
            int n2 = num2.charAt(j) - '0';
            // 截取的两个数相乘 并加上之前的进位(之前相乘的积的第一位)
            int sum = (res[i + j + 1] + n1 * n2);
            // 当前所有积相加的第二位
            res[i + j + 1] = sum % 10;
            // 当前所有积相加的第一位,作为下一个积的个位
            res[i + j] += sum / 10;
        }
    }

    StringBuilder result = new StringBuilder();
    for (int i = 0; i < res.length; i++) {
        if (i == 0 && res[i] == 0) continue;
        result.append(res[i]);
    }
    return result.toString();
}

在这里插入图片描述

93. 复原IP地址

/**
 * ip格式:xxx.xxx.xxx.xxx
 * 每一个部分大小在[0 255]
 * 1.1.1.1  ~  255.255.255.255  字符串最小长度为4,最大长度为12
 * 每个部分可能是:1位 2位 3位
 * 如果是两位开头不能是 0
 */
class Solution {
    public List<String> restoreIpAddresses(String s) {
        ArrayList<String> res = new ArrayList<>();
        if (s.length() < 4 || s.length() > 12) return res;
        dfs(res, s, "", 0);
        return res;
    }

    /**
     * 1. 定义
     * @param res            ip结果
     * @param remainStr      剩余字符串
     * @param ipStr          已经截取的ip字符串
     * @param index          分成的部分(0,1,2,3)
     */
    public void dfs(List<String> res, String remainStr, String ipStr, int index) {
        // 3. 出口,下面两个判断不能换位置  (.1.1.1.1)
        if (index == 4 && remainStr.length() == 0) res.add(ipStr.substring(1));
        if (index == 4 || remainStr.length() == 0) return;
        // 2. 拆解
        // 一位数
        dfs(res, remainStr.substring(1), ipStr + "." + remainStr.substring(0, 1), index + 1);
        // 两位数
        if (remainStr.charAt(0) != '0' && remainStr.length() > 1) {
            dfs(res, remainStr.substring(2), ipStr + "." + remainStr.substring(0, 2), index + 1);
            // 三位数
            if (remainStr.length() > 2 && Integer.valueOf(remainStr.substring(0, 3)) <= 255) {
                dfs(res, remainStr.substring(3), ipStr + "." + remainStr.substring(0, 3), index + 1);
            }
        }
    }
}

678. 有效的括号字符串

在这里插入图片描述

class Solution {
    public static void main(String[] args) {
        String str1 = "**)))";
        String str2 = "((()*";
        System.out.println(checkValidString(str2));
    }
    public static boolean checkValidString(String s) {
        int minOp = 0; // 必须被匹配的最小左括号数
        int maxOp = 0; // 可能的最大左括号数
        for (char c : s.toCharArray()) {

            // 如果是左括号,则强制匹配的左括号数加一
            if (c == '(') minOp++;  // (
            // 不是左括号
            else minOp--;

            // 如果是左括号或者*,可能的最大左括号数加一
            if (c != ')') maxOp++; // ( or *
            // 右括号
            else maxOp--; // )

            // maxOp < 0 则右括号已经比左括号多了
            if (maxOp < 0) return false;
            minOp = Math.max(0, minOp);
        }
        return minOp == 0;
    }
}

在这里插入图片描述

758. 字符串中的加粗单词

在这里插入图片描述
方法一

public static String boldWords(String[] words, String S) {
    // 字典里面每个单词的最大长度,根据题意最大为 10
    int kMaxWordLen = 10;
    int n = S.length();
    HashSet<String> set = new HashSet<>();
    // 标记数组:用于标记S中那些位置的字符应该被加粗,加粗位置的值置 1
    int[] bold = new int[n];
    for (String word : words) {
        set.add(word);
    }
    // 遍历字符串中的每个字符
    for (int i = 0; i < n; i++) {
        // 以当前字符开头的子串,子串的长度依次递减
        // 如果已经遍历到 S 的快结束位置了,子串的长度不够 10,最大应该为 n - i
        for (int l = Math.min(n - i, kMaxWordLen); l >= 1; l--) {
            // 在 S中截取从 i开始共 l个子串
            if (set.contains(S.substring(i, i + l))) {
                // 如果在set集合中,就把标记数组的 i 到 i+l 位置赋值为 1 
                for (int j = 0; j < l; j++) {
                    bold[j + i] = 1;
                }
                // 因为是先比较长的,所以短的肯定也在字典里面
                break;
            }
        }
    }
    StringBuilder res = new StringBuilder();
    for (int i = 0; i < n; i++) {
        // 当前位置的需要加粗,并且以 [...0,1...]开始 或者 [1,...] 开始就加上<b>
        if (bold[i] == 1 && (i == 0 || bold[i - 1] != 1)) res.append("<b>");
        res.append(S.charAt(i));
        // 以[...,1,0] 或者 [...,1]结尾就加上</b>
        if (bold[i] == 1 && (i == n - 1 || bold[i + 1] != 1)) res.append("</b>");

    }
    return res.toString();
}

方法二

public static String boldWords(String[] words, String S) {
    int[] s = new int[S.length() + 1];
    // 遍历字典
    for (String w : words) {
        int i = 0;
        // w 从 S 的 i 位置开始第一次在 S 中出现的索引,如果从 i 位置开始没有找到则返回 -1
        while ((i = S.indexOf(w, i)) >= 0) {
            s[i]++;
            s[i + w.length()]--;
            i++;
        }
    }
    StringBuilder res = new StringBuilder();
    int pre = 0, sum = 0;
    for (int i = 0; i <= S.length(); i++) {
        sum += s[i];
        if (sum > 0 && pre == 0) res.append("<b>");
        if (sum == 0 && pre > 0) res.append("</b>");
        if (i < S.length()) res.append(S.charAt(i));
        pre = sum;
    }
    return res.toString();
}

1249. 移除无效的括号

在这里插入图片描述

class Solution {
    public static void main(String[] args) {
        String str = "a(b)c(d)e)";
        System.out.println(minRemoveToMakeValid(str));
    }
    public static String minRemoveToMakeValid(String s) {
        if (s == null || s.length() == 0) return "";
        //
        int count = 0;
        StringBuilder sb = new StringBuilder();
        // 从左往右,去掉多余的右括号,左括号可能多余
        for (char c : s.toCharArray()) {
            if (c == '(') {
                count++;
            } else if (c == ')') {
                // 左括号数量为 0,跳过当前右括号
                if (count == 0) {
                    continue;
                }
                count--;
            }
            sb.append(c);
        }

        // 从右往左 count >= 0
        StringBuilder res = new StringBuilder();
        for (int i = sb.length() - 1; i >= 0 ; i--) {
            // 遍历到左括号,如果未匹配的左括号数量不为 0,跳过当前左括号
            if (sb.charAt(i) == '(' && count-- > 0) {
                continue;
            }
            res.append(sb.charAt(i));
        }
        return res.reverse().toString();

    }
}

删除多余的空格

输入:__a_b__c___d
输出:a_b_c_d

public String removeExtraSpace(String s) {
    StringBuilder res = new StringBuilder();
    for (int i = 0; i < s.length(); ) {
        // 如果当前字符是一个空格,进入这个if,表明至少有一个空格
        if (s.charAt(i) == ' ') {
            // 如果不是字符串的第一个字符,为 true
            boolean isntBegin = i != 0;
            // 如果是空格就继续判断下一个,直到不是空格
            while (i < s.length() && s.charAt(i) == ' ') {
                i++;
            }
            // 当前判断的不是第一个字符,在结果上追加一个空格
            // 经过上面的判断可能跳过很多空格,可能没有跳过(只有一个空格)
            if (isntBegin && i < s.length()) {
                res.append(' ');
            }
        }

        // 如果当前字符不是空格,添加到结果中,判断下一个是否是空格
        // 如果遇到空格就跳出循环,由上面的if判断来消除多余的空格
        while (i < s.length() && s.charAt(i) != ' ') {
            res.append(s.charAt(i));
            i++;
        }
    }
    return res.toString();
}



































































评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值