一刷剑指Offer:字符串专题

三、字符串专题

1、剑指Offer_05:替换空格

题目:请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。

public class Solution {
    public String replaceSpace(StringBuffer str) {
        /**
         * 1、创建一个StringBuilder
         * 2、遍历字符串中的每一个字符c,如果是空格,就拼接%20
         * 3、如果不是空格,就拼接c
         */
        String s = str.toString();
        StringBuilder sb = new StringBuilder();
        for(char c:s.toCharArray()){
            if(c==' '){
                sb.append("%20");
            }else{
                sb.append(c);
            }
        }
        return sb.toString();
    }
}

2、剑指Offer_19:正则表达式匹配

题目:给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 ‘.’ 和 ‘*’ 的正则表达式匹配。

‘.’ 匹配任意单个字符
‘*’ 匹配零个或多个前面的那一个元素

所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。

/**
 * 本题考查动态规划,困难题
 *
 * 时间复杂度:O(m*n)
 * 空间复杂度:O(m*n)
 */
public class Solution {
    public boolean match(char[] str, char[] pattern) {
        /**
         * 动态规划:定义boolean dp[i][j]的含义就是s[0-i]与p[0-j]是都匹配
         * 分情况考虑:
         * 1、pattern[j]==str[i]:dp[i][j] = dp[i-1][j-1];
         * 2、pattern[j]=='.':dp[i][j] = dp[i-1][j-1];
         * 3、pattern[j]=='*':
         *      1、如果pattern[j-1]!=str[i] :dp[i][j] = dp[i][j-2]
         *           s = "ba"、p = "bc*a"-->p="ba"
         *      2、如果 pattern[j-1]==str[i] 或者 pattern[j-1]!='.'
         *          1、如果 '.*' 匹配1个a:dp[i][j]=dp[i][j-1];   
         *              s = "ba"、p = "b.*a"   --->p="baa"
         *          2、如果 '.*' 匹配多个a:dp[i][j] = dp[i-1][j];  
         *              s = "baaa"、p = "b.*a" --->p="baaaa"
         *          3、如果 '.*' 匹配0个a:dp[i][j] = dp[i][j-2];   
         *              s = "ba"、p = "b.*a"   -->p="ba"
         */
        if(str==null || pattern==null){
            return false;
        }

        boolean[][] dp = new boolean[str.length+1][pattern.length+1];
        dp[0][0] = true;

        /**
         * 要将pattern[j]=='*' 这种情况单独拿出来处理一下,c*删除的时候,dp[0][j+1] = true
         * str="aab"  pattern="c*aab"-->pattern="aab"
         */
        for(int j=0;j<pattern.length;j++){
            if(pattern[j]=='*' && dp[0][j-1]){
                dp[0][j+1] = true;
            }
        }

        for(int i=0;i<str.length;i++){
            for(int j=0;j<pattern.length;j++){
                if(pattern[j]==str[i]){
                    dp[i+1][j+1] = dp[i][j];
                }
                if(pattern[j]=='.'){
                    dp[i+1][j+1] = dp[i][j];
                }
                if(pattern[j]=='*'){
                    if(pattern[j-1]!=str[i] && pattern[j-1]!='.'){
                        dp[i+1][j+1] = dp[i+1][j-1];
                    }else{
                        dp[i+1][j+1] = (dp[i+1][j]) || (dp[i][j+1]) || (dp[i+1][j-1]);
                    }
                }
            }
        }
        return dp[str.length][pattern.length];
    }
}

3、剑指Offer_20:表示数值的字符串

题目:请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100",“5e2”,"-123",“3.1416"和”-1E-16"都表示数值。 但是"12e",“1a3.14”,“1.2.3”,"±5"和"12e+4.3"都不是。

public class Solution {
    public boolean isNumeric(char[] str) {
        //将char数组转换为String对象
        String s = new String(str);
        //写这个条件是因为5.6f视为不是数值,但是转换的时候是数值
        if(s.endsWith("f") || s.endsWith("d") || s.endsWith("F") || s.endsWith("D")){
            return false;   
        }
        try {
            Double.parseDouble(s);
        } catch (NumberFormatException e) {
            return false;
        }
        return true;
    }
}

4、剑指Offer_23:字符流中第一个不重复的字符

题目:请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。

import java.util.LinkedList;
import java.util.Queue;

public class Solution {
    
    //定义一个数组用来记录每个字符出现的次数
    int[] arr = new int[128];
    
    //定义一个队列用来找到第一个不重复的数字,先进先出
    Queue<Character> queue = new LinkedList<>();
    
    //Insert one char from stringstream
    public void Insert(char ch) {
        arr[ch]++;
        if(arr[ch]>1){
            return;
        }
        queue.add(ch);
    }
    //return the first appearence once char in current stringstream
    public char FirstAppearingOnce() {
        while (!queue.isEmpty() && arr[queue.peek()]>1){
            queue.poll();
        }
        if(queue.isEmpty()){
            return '#';
        }
        return queue.peek();
    }
}

5、剑指Offer_38:字符串的排列

题目:输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。

思路:即通过字符交换,先固定第 1位字符( n种情况)、再固定第 2位字符( n−1 种情况)、… 、最后固定第 n 位字符( 1种情况)。

aple,如何确定字符转在每一位的字符?
1、确定第1位字符:a与后面p交换确定p、a与后面l交换确定l、a与后面e交换确定e、最终为a、p、l、e
2、确定第2位字符:排除a后,p与l交换确定l、p与e交换确定e ,第二位字符为p、l、e
排除p后,a与l交换确定l、a与e交换确定e ,第二位字符为a、l、e
…以此类推

扫描二维码关注公众号,回复: 11211083 查看本文章

3、递归回溯到当前层的时候,为了消除当前层去递归的时候进行交换字符的影响,需要再交换为原来的位置。

比如 aple:

a与p交换,得到第一位的字符p、继续向下递归寻找第二位的字符、第三位的字符、、、、当回到第一位时需要确定第一位的下一个字符时,a需要与l交换,但是此时字符串为pale,变成了p与l交换,为了消除这种影响,需要在递归回溯到本层时再交换回来。

4、如果字符串中有重复字符,那么就会出现重复排列方案,为排除重复方案,需在固定某位字符时,保证 “每种字符只在此位固定一次” ,即遇到重复字符时不交换,直接跳过。或者直接把左右排列情况都找出来,最后统一去除重复方案。

在这里插入图片描述

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;

public class Solution {
    public ArrayList<String> Permutation(String str) {
        /**
         * 递归回溯算法:
         * 1、通过字符串交换先固定第一位,让当前字符与后面的每一位进行交换
         *      aple:得出第一位的可能字符为a、p、l、e
         * 2、通过递归去固定第2位、第3位、、、、
         * 3、递归回溯到第一位时,需要消除交换造成的影响,将字符交换回来
         * 4、递归终止的条件是固定到最后一位时
         * 5、通过HashSet去重
         * 6、通过Collections集合类进行排序
         */

        //1、将字符串转换为字符数组
        char[] strArr = str.toCharArray();
        //2、定义一个集合用来存放返回结果
        ArrayList<String> list = new ArrayList<>();
        dfs(strArr,list,0,str.length());
        //3、去重
        list = new ArrayList<String>(new HashSet<String>(list));
        //4、排序
        Collections.sort(list);
        return list;
    }

    /**
     *
     * @param strArr 传入的字符数组
     * @param list   用来存放结果的集合
     * @param index  固定第index位
     * @param length 数组的长度
     */
    public void dfs(char[] strArr,ArrayList<String> list,int index,int length){
        //1、递归终止的条件,固定到最后一位时
        if(index==length-1){
            String res = change(strArr);
            //添加排列方案
            list.add(res);
            return;
        }

        for(int i=index;i<length;i++) {
            //2、固定第index位,将strArr[i]固定在第index位
            char temp = strArr[i];
            strArr[i] = strArr[index];
            strArr[index] = temp;
            //3、递归固定第index+1位
            dfs(strArr, list, index + 1, length);
            //4、递归回溯到本层时需要消除字符交换的影响,再将字符交换回来,让原来的字符与下一位字符交换
            temp = strArr[i];
            strArr[i] = strArr[index];
            strArr[index] = temp;
        }
    }
    
    public String change(char[] a){
        StringBuilder res = new StringBuilder();
        for(char value: a){
            res.append(value);
        }
        return res.toString();
    }
}

6、剑指Offer_45:把数组排成最小的数

题目:输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。

/**
 * 在排序时传入一个自定义的Comparator实现,重新定义String类内的排序方法,
 * 若拼接s1+s2 > s2+s1,那么把s2在拼接时放在前面,以此类推,将整个String列表排序后再拼接起来。
 * 如:30、3--- 303<330 ---303
 */
public class Solution {
    public String PrintMinNumber(int [] numbers) {

        /**
         * 1、将整型数组转换为字符串数组
         */
        String[] str = new String[numbers.length];
        for(int i=0;i<numbers.length;i++){
            str[i] = String.valueOf(numbers[i]);
        }

        /**
         * 2、自定义比较器比较拼接后字符串
         *  public int compareTo( NumberSubClass referenceName )
         *  如果指定的数大于参数返回1: System.out.println(5.compareTo(3)); //1  排序为:3、5
         */
        Arrays.sort(str, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return (o1+o2).compareTo(o2+o1);
            }
        });

        /**
         *3、将字符串数组拼接成字符串
         */
        StringBuilder sb = new StringBuilder();
        for(int i=0;i<str.length;i++){
            sb.append(str[i]);
        }

        return sb.toString();
    }
}

7、剑指Offer_67:把字符串转换成整数

题目:写一个函数 StrToInt,实现把字符串转换成整数这个功能。不能使用 atoi 或者其他类似的库函数。

该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。

当我们寻找到的第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字组合起来,作为该整数的正负号;

假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成整数。

该字符串除了有效的整数部分之后也可能会存在多余的字符,这些字符可以被忽略,它们对于函数不应该造成影响。

注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换。

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

说明:假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为 [−231, 231 − 1]。如果数值超过这个范围,请返回 INT_MAX (231 − 1) 或 INT_MIN (−231) 。

在这里插入图片描述

public class Solution {
    /**
     * 1、字符串的前面出现空格,丢弃
     * 2、三种情况,即 ''+'' , ''−'' , ''无符号",新建一个变量保存符号位,返回前判断正负。
     * 3、遇到首个非空字符,直接返回
     * 4、如果是数字字符:
     *      1、将字符转换成数字:此 "数字字符的ASCII" 与 "0的ASCII相减"
     *      2、将字符进行拼接res = 10*res-ascii(c)-ascii(0)
     */
    public int StrToInt(String str) {

        //1、使用字符串的trim()函数取出字符串两端的空格
        char[] charArray = str.trim().toCharArray();

        if(charArray.length==0){
            return 0;
        }

        int i=1;
        //2、定义变量保存符号位
        int sign = 1;
        //说明符号位为负
        if(charArray[0]=='-'){
            sign=-1;
        }else if(charArray[0]!='+'){
            i=0;
        }

        long res = 0;
        //3、将字符串转换为整数
        for(int j=i;j<charArray.length;j++) {
            if (charArray[j] < '0' || charArray[j] > '9') {
                break;
            }

            res = res * 10 + (charArray[j] - '0');
            //判断res是否超出整型范围
            if (res > Integer.MAX_VALUE) {
                return sign == 1 ? Integer.MAX_VALUE : Integer.MIN_VALUE;
            }
        }
        return sign * (int)res;
    }
}
原创文章 723 获赞 153 访问量 18万+

猜你喜欢

转载自blog.csdn.net/qq_42764468/article/details/105850407