剑指Offer______字符串

481 阅读5分钟

5.替换空格

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

解题思路:\color{#008000}{解题思路:}从头到尾扫描字符串,每一次碰到空格的时候做替换。由于是把1个字符替换成3个字符,我们必须要把后面的所有字符都后移两个字节,否则就有两个字符被覆盖了。故假设字符串的长度是n,对每个空格字符,需要移动后面O(n)个字符,因此对含有面O(n)个空格的字符串而言总的时间效率是面O(n^2)。
先遍历一遍字符串统计出字符串中空格的总长度,因每次替换一个空格长度+2;故所需的总长度为oldLenth+blankCount*2;上次我们的时间复杂度达到n的平方,是因为每次替换一个空格,我们的后面的字符串都要移动造成的,故这次我们从后往前移动,使字符在第一次就到达自己的位置上,故这样做的话时间复杂度为O(n)

public class Test{
public String replaceSpace(StringBuffer str) {
        if (str.length()==0||str==null){
            return "";
        }
        int space = 0;//空格数
        for (int i = 0; i < str.length(); i++) {
            if (str.charAt(i) == ' ') {
                space++;//先统计字符串中空格数
            }
        }
        int oldLength = str.length();  //原来字符串长度
        int indexOld = oldLength-1;//原来字符串最后一个字符下标
        int newLength = oldLength+space*2; //新字符串长度(包括空格)
        int indexNew = newLength-1;//要赋值的新字符串的下标
        str.setLength(newLength); //扩大到转换成%20之后的长度,防止下标越界
        while(indexNew>-1){
            if(space==0){ //如果没有空格,不需要复制,直接跳出
                break;
            }
            if (str.charAt(indexOld)==' '){
                indexOld--;
                str.setCharAt(indexNew--,'%');
                str.setCharAt(indexNew--,'0');
                str.setCharAt(indexNew--,'2');
                space--;
            }else{
                str.setCharAt(indexNew--,str.charAt(indexOld--));
            }
        }
        return str.toString();
    }
    
    public static void main(String[] args) {
            Test s=new Test();
            StringBuffer sb=new StringBuffer();
            sb.append("We are Happy.");
            System.out.println(s.replaceSpace(sb));
        }
}

复制代码当对字符串进行修改的时候,需要使用 StringBuffer和StringBuilder类
StringBuilder相较于StringBuffer有速度优势,但StringBuilder的方法不是线程安全的(不能同步访问)

StringBuffer的方法:

StringBuffer append(String s);将指定的字符串追加到此字符序列
char charAt(int index);返回此序列中指定索引处的charint length();返回长度(字符数)
void setCharAt(int index, char ch);将给定索引处的字符设置为ch
void setLength(int newLength);设置字符序列的长度
String toString();返回此序列中数据的字符串表示形式

static关键字最基本的用法是:

1、被static修饰的变量属于类变量,可以通过类名.变量名直接引用,而不需要new出一个类来
2、被static修饰的方法属于类方法,可以通过类名.方法名直接引用,而不需要new出一个类来

a++和++a的区别:

a++就是先用a,再+1操作;++a就是先+1操作,再用a
a++,则返回值为旧值,如果是++a, 则返回值为新值  

38.字符串的排列

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

解题思路:\color{#008000}{解题思路:} 把需要全排列的字符串分为两部分:

字符串的第一个字符
第一个字符后面的所有字符(待全排列字符)

首先,将第一个字符与后面字符依次交换位置,然后固定第一个字符,再将后面的字符重复上述操作,直到字符串末尾。过程如下

import java.util.*;
public class Solution {
    public ArrayList<String> Permutation(String str) {
            if(str==null){
                return null;
            }
            char[] c=str.toCharArray();//把字符串转换为数组
            ArrayList<String> list=new ArrayList<String>();
            PermutationCore(c,0,list);
            Collections.sort(list);//按字典排序对结果排序

            return list;
    }

    private void PermutationCore(char[] c, int begin, ArrayList<String> list) {
            //当begin指向了最后一个字符
            if (begin==c.length-1){
                //将数组转换为字符串
                list.add(String.valueOf(c)); //这个操作就是【回溯】,由下到上依次把结果保存到list中

            // 当begin不指向最后一个时,begin代表我们做排列操作的字符串的第一个字符
            }else {
                for(int i=begin; i<c.length; i++){
                    //相同字符处理
                    if(i != begin && c[i] == c[begin]){
                        continue;
                    }
                    //将第一个字符begin与后面每一个字符交换位置
                    swap(c,begin,i);
                    //对每一个交换后的字符集,固定第一个字符,从第二个字符开始继续执行以上操作
                    PermutationCore(c,begin+1,list);
                    //还原到交换前的状态,否则影响后续循环,也就是再将之前交换的字符交换回来,以便于第一个字符再与其他字符进行交换
                    swap(c,begin,i);
                }
            }
    }
    /*交换*/
    private void swap(char[] c, int begin, int i) {
            char temp=c[i];
            c[i]=c[begin];
            c[begin]=temp;
    }
}
toCharArray( ); 将字符串对象中的字符转换为一个字符数组
Collections.sort( ); Collections是一个工具类,sort是其中的静态方法,是用来对List类型进行排序的
String.valueOf(char[] c); 将 char 数组转换成字符串

50.第一个只出现一次的字符

(牛客)题目描述: 在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写)

解题思路:\color{#008000}{解题思路:} Map集合

import java.util.*;
public class Solution {
    public int FirstNotRepeatingChar(String str) {
        if(str.length()==0){
            return -1;
        }
        Map<Character,Integer> map=new HashMap<>();	//声明Map集合,K值存放字符,V值存放该字符出现次数
        
        for(int i=0;i<str.length();i++){	//首先遍历该字符串,统计各个字符出现的次数
            map.put(str.charAt(i),map.getOrDefault(str.charAt(i),0)+1);
        }
        for(int i=0;i<str.length();i++){	//然后再遍历字符串,统计出现次数为1的字符
            if(map.get(str.charAt(i))==1){
                return i;
            }
        }
        return -1;
    }
}

map.getOrDefault(key,defaultValue)\color{red}{map.getOrDefault(key, defaultValue)}
当Map集合中有这个key时,就获得这个key值对应的Value,如果没有就使用默认值defaultValue

(LeetCode)**题目描述:**在字符串 s 中找出第一个只出现一次的字符。如果没有,返回一个单空格。 s 只包含小写字母。

示例:

s = "abaccdeff"
返回 "b"

s = ""
返回 " "

class Solution {
    public char firstUniqChar(String s) {
        if(s.length()==0){
            return ' ';
        }
        Map<Character,Integer> map=new HashMap<>();	//声明Map集合,K值存放字符,V值存放该字符出现次数
        
        for(int i=0;i<s.length();i++){	//首先遍历该字符串,统计各个字符出现的次数
            map.put(s.charAt(i),map.getOrDefault(s.charAt(i),0)+1);
        }
        for(int i=0;i<s.length();i++){	//然后再遍历字符串,统计出现次数为1的字符
            if(map.get(s.charAt(i))==1){
                return s.charAt(i);
            }
        }
        return ' ';
    }
}

单空格表示为' '

58.翻转单词顺序

题目描述:
(牛客)输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。为简单起见,标点符号和普通字母一样处理。例如输入字符串"I am a student. ",则输出"student. a am I"

输入: "the sky is blue
输出: "blue is sky the"

解题思路1\color{#008000}{解题思路1:} 先反转整个字符串得到eulb si yks eht,再对每个单词进行反转得到blue is sky the

public class Solution {
    public String ReverseSentence(String str) {
        String flipstr=new StringBuilder(str).reverse().toString();	//对整个字符串翻转
        StringBuilder res=new StringBuilder();	//用来遍历每个单词
        StringBuilder ans=new StringBuilder();	//用来保存结果
        for(int i=0;i<flipstr.length();i++){
            if(ReverStr.charAt(i)==' '){
                ans.append(res.reverse().toString()).append(" ");
                res=new StringBuilder();
            }else{
                res.append(flipstr.charAt(i));
            }
        }
       ans.append(res.reverse().toString());	//最后一个单词的最后一个字母遍历之后,没有空格,
       程序直接就跳出了for循环了,所以“手动”将最后一个单词翻转结果保存到ans中
       return ans.toString();
    }
}

reverse()将字符串反转

解题思路2\color{#008000}{解题思路2:}先把每个单词都进行反转,再对字符串全部反转(定义reverse函数)

public class Solution {
    public String ReverseSentence(String str) {
      if(str == null || str.length() == 0) {
        	return str;
        }
        
        char[] arr = str.toCharArray();
        int len = arr.length;
        
        int start = 0;
        int end = 0;
        
        // 反转每个单词
        while (start < len && end < len) {
        	while (end < len && arr[end] != ' ') {
        		end++;
        	}
        	
        	reverse(arr, start, end - 1);
        	
        	start = end + 1;
        	end = start;
        }
        
        // 整个反转
        reverse(arr, 0, len-1);
        
        return new String(arr);
    }
	
    private void reverse(char[] arr, int i, int j) {
    	if (i == j) {
    		return;
    	}
    	while (i < j) {
    		char tmp = arr[i];
    		arr[i] = arr[j];
    		arr[j] = tmp;
    		
    		i++;
    		j--;
    	}
    }
}

(LeetCode)

public class Solution {
String flipStr = new StringBuilder(str.trim()).reverse().toString(); // 第一次翻转
        StringBuilder res = new StringBuilder(); // 用来遍历每一个单词
        StringBuilder ans = new StringBuilder(); // 用来保存结果
        for (int i = 0; i < flipStr.length(); i++) {
            if (flipStr.charAt(i) == ' ') {
                if (res.length() > 0) {
                    ans.append(ans.length() == 0 ? res.reverse() : " " + res.reverse());
                    res = new StringBuilder();
                }
            } else {
                res.append(flipStr.charAt(i));
            }
        }
        if (res.length() > 0) {
            ans.append(ans.length() == 0 ? res.reverse() : " " + res.reverse());
        }
        return ans.toString();
    }
}

trim()的作用是去掉字符串两端的多余的空格

**58.左旋转字符串 **

**题目描述:**字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如,输入字符串"abcdefg"和数字2,该函数将返回左旋转两位得到的结果"cdefgab"。

解题思路:\color{#008000}{解题思路:}先整体翻转,再利用字符串的截取对两个子序列翻转

public class Main43 {
    public String LeftRotateString(String str,int n) {
        if (str.length() == 0) {
            return "";
        }
        StringBuilder flipStr = new StringBuilder(str).reverse(); // 第一次翻转
        String str1 = flipStr.substring(0, flipStr.length() - n); // 取出第一个子序列
        String str2 = flipStr.substring(flipStr.length() - n); // 取出第二个子序列
        return new StringBuilder(str1).reverse().append(new StringBuilder(str2).reverse()).toString(); // 第二次翻转
    }
}

substring()方法\color{red}{substring( ) 方法}

String str1 = flipStr.substring(int beginIndex); //截取flipStr中从首字母开始长度为beginIndex的字符串,将剩余部分赋给str1,结束索引(不包含)

String str2 = flipStr.substring(int beginIndex, int endIndex); //截取从beginIndex到endIndex的字符串赋给str2,结束索引(不包含) append()方法\color{red}{append()方法}
append方法的作用是在一个StringBuffer对象后面添加字符串

20.表示数值的字符串

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

解题思路:\color{#008000}{解题思路:}首先,搞清楚组成数字的4个部分:

数字本身 0-9
数字符号 + 或 -
小数点 .
e或E

然后,判断每一个数字位,判断时需要考虑3种情况:

1.若是正负号,必须出现在首位或e的后一位
2.若是e,之前必须出现过数字且e不能重复
3.若是.,之前必须没出现过.和e

最后,需要设置4个标识位:

boolean pointSeen = false; //是否出现过小数点“.”
boolean eSeen =false; //是否出现过指数标志“e”
boolean numberSeen = false; //是否出现过数字
boolean numberBeforeE = false; //e之前是否有数字

public class Solution {
    public boolean isNumeric(char[] str) {
        if (str == null || str.length == 0)
            return false;
        // 记录是否出现过数字
        boolean numberSeen = false;
        // 是否出现过小数点
        boolean pointSeen = false;
        // 是否出现过指数标志e
        boolean eSeen = false;
        // e前是否出现过数字
        boolean numberBeforeE = false;
 
        for (int i = 0; i < str.length; i++) {
            char c = str[i];
            // 如果是数字
            if (c >= '0' && c <= '9') {
                numberSeen = true;
                numberBeforeE = true;//对于输入为纯数字的情况,要先设为true
                // 如果是小数点
            } else if (c == '.') {
                if (pointSeen || eSeen)
                    return false;
                pointSeen = true;
                // 如果是e || E,必须没有出现过且出现过数字
            } else if (c == 'e' || c == 'E') {
                if (eSeen || !numberSeen)
                    return false;
                eSeen = true;
                numberBeforeE = false;
                // 如果出现正负号,必须是首位,或者前一位是e || E
            } else if (c == '+' || c == '-') {
                if (i != 0 && str[i - 1] != 'e' && str[i - 1] != 'E')
                    return false;
            } else
                return false;
        }
        return numberSeen && numberBeforeE;

    }
}

19.正则表达式匹配

**题目描述:**请实现一个函数用来匹配包括.*的正则表达式 ,模式中的字符.表示任意一个字符,而*表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串" aaa "与模式" a.a "和" abaca "匹配,但是与" aa.a "和" ab*a "均不匹配。

解题思路:\color{#008000}{解题思路:}

把字符串转换成整数

**题目描述:**将一个字符串转换成一个整数,要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0

解题思路:\color{#008000}{解题思路:}

public class Solution {
    public int StrToInt(String str) {
     	if(str.length()==0){
            return 0;
        }
        
        int len=str.length();
        int index=0;
        while(index<len){	//去除字符串首部的空格
            if(str.charAt(index)==' '){
                index++;
            }else{
                break;
            }
        }
        int positive=0;	//用于记录当前数字是正数还是负数
        long ans=0;	//用于保存结果
        while(index<len){
            if(str.charAt(index)=='-'||str.charAt(index)=='+'){
                if(positive!=0){	//对于“-123-1”第二个“-”是非法字符
                    return 0;
                }
                positive=str.charAt(index)=='-'?-1:1;
            }
            else if(str.charAt(index)>='0'&&str.charAt(index)<='9'){
                ans=ans*10+str.charAt(index)-'0';
                if(judge(ans,positive)){	//对ans是否溢出int类型做判断
                    return 0;
                }
            }
            else{
                return 0;	// 既不是数字,也不是正负号,那就是其他字符了,返回0
            }
            index++;
        }
        return positive==-1?(int)ans*(-1):(int)ans;
    }
    private boolean judge(long ans,int positive){
        if(positive==-1){
            if(ans*(-1)<Integer.MIN_VALUE){
                return true;
            }
        }
        else{
            if(ans>Integer.MAX_VALUE){
                return true;
            }
        }return false;
    }
}