算法学习-Java字符串处理及其常用API,把这种题目刷熟练的男人真的很帅(持续更新中)


字符串一直是面试考察的重点,其破解之道的前提是对相关的api非常熟悉,然后在此基础上运用算法思想解题。本专题就从零开始,记录自己的字符串处理刷题路径,同时记录下相关api,以便对字符串处理游刃有余。

相关知识

1. String常用api
str.length() //求字符串str长度
str.charAt(index) //index处的字符
str.contains(anotherStr) //判断str中是否包含anotherStr
str.startsWith(searchWord) //判断str是否以searchword开头
Character.isDigit(char c) //可以判断c是不是数字
Character.isLetterOrDigit(char c)//判断c是否为数字或者字母
Character.toLowerCase(char c) //将c转换为小写(如果有的话,否则字符不变,如'_')
str.substring(start,end) //其中[start,end),参考下面3.
str.substring(start) //截取start及其以后的所有字符,参考下面3.
str.replace(char oldchar,char newchar) //将字符串中的oldchar替换为newchar
curstr.equals(targetstr) //比较当前curstr和targetstr是否相同
Objects.equals(curstr,targetstr) //同上比较字符串,不过可以避免curstr为空
2. StringBuilder常用方法
//StringBuilder res转String
sb.toString()
sb.substring()

sb.charAt(int index) //检索字符
sb.append(String str) //追加字符串
sb.append(char c) //追加字符
sb.append(int a) //追加a的字符形式
sb.deletecharAt(int index) //删除指定位置上的字符
sb.reverse() //对字符串进行翻转
3. String字符串截取 String.substring
String.substring(start,end) //其中[start,end)
String.substring(start) //截取start及其以后的所有字符

在这里插入图片描述

4. 字符串字典序比较 str.compareTo(a)

在这里插入图片描述

5. 字符串与数字间相互转换

字符串转数字

String str="123";

Integer num1=new Integer(str);
int num2= Integer.parseInt(str);
Integer num3=Integer.valueOf(str);

数字转字符串

Integer.toString(n)
6. char[]、String转换
//char[]转String
String.valueOf(char[])
//String转char[]
char[] a=s.toCharArray()
7. 正则表达式 s.matches(“”)

参考 正则表达式30分钟入门教程 (deerchao.cn)

8. 字符串分割 String.split()

参考java split方法怎么用
需要转义的字符如|.+*^?[/{}()$,转义方法为字符前面加上\\。反斜杠\ 在字符串中也是需要转义的,字符串中想要使用一个反斜杠需要这么写\\
注意:当split的字符在字符串两侧时,会划分出来空字符串""

9. 格式保留问题 String.format()

参考JAVA字符串格式化——String.format()的使用
在这里插入图片描述

相关题目

1455.检查单词是否为句中其他单词的前缀

可以采用String的api startWith(searchWord)或者直接暴力枚举。

class Solution {
    public int isPrefixOfWord(String sentence, String searchWord) {
        String[] words=sentence.split(" ");
        for(int i=0;i<words.length;i++){
            String word=words[i];
            int n=word.length();
            int m=searchWord.length();
            if(m>n) continue;
            boolean flag=true;
            for(int j=0;j<m&&flag;j++){
                if(word.charAt(j)!=searchWord.charAt(j)) flag=false;
            }
            if(flag) return i+1;
        }
        return -1;
    }
}
1408.数组中的字符串匹配

暴力枚举就好了

class Solution {
    public List<String> stringMatching(String[] words) {
        ArrayList<String> ans=new ArrayList<>();
        int len=words.length;
        for(int i=0;i<len;i++){
            for(int j=0;j<len;j++){
                if(i!=j&&words[j].contains(words[i])){
                    ans.add(words[i]);
                    //满足一个子字符串的要求就可以break
                    break;
                }
            }
        }
        return ans;
    }
}
179.最大数

关键在于sort中compareTo的运用,通过字典序的传递性,a在b前&&b在c前则最终a在c前,从而达到整体数字最大。

class Solution {
    public String largestNumber(int[] nums) {
        int len=nums.length;
        String[] s=new String[len];
        for(int i=0;i<len;i++){
            s[i]=nums[i]+"";
        }
        Arrays.sort(s,(a,b)->(b+a).compareTo(a+b));
        String res=String.join("",s);
        return res.charAt(0)=='0'?"0":res; // 0能放在第一个就说明后面必然全是0
    }
}
761.特殊的二进制序列

参考宫水三叶的题解s可以被可恰好划分为多个特殊的子串item,当且仅当某个子串满足1和0个数相等就可以,因为原始序列s可以保证第二个性质。第二个性质也表明,特殊子串item必然为1...0的模式。

因此可将本题构造分两步进行:1.对每个item进行重排,使其本身调整为字典序最大;2.对不同item之间进行重排,使最终s整体字典序最大。对于1,我们可以对 item 中的 1...0 中的非边缘部分进行调整(递归处理子串部分),同时用list将该子串进行收集。对于2,我们利用对list进行重排,重排参考179.最大数,最终组合出结果。

class Solution {
    public String makeLargestSpecial(String s) {
        int len=s.length();
        return dfs(s,0,len-1);
    }
    public String dfs(String s,int start,int end){
        if(start>end) return "";
        ArrayList<String> list=new ArrayList<>();
        int count=0;
        int split=start;
        for(int i=start;i<=end;i++){
            if(s.charAt(i)=='1') count++;
            else count--;
            if(count==0){
                //步骤一,item本身重排
                list.add("1"+dfs(s,split+1,i-1)+"0");
                split=i+1;
            }
        }
        //步骤二,item整体重排
        //对ArrayList调用Collections.sort()
        Collections.sort(list,(a,b)->(b+a).compareTo(a+b));
        StringBuilder sb=new StringBuilder();
        for(String ss:list){
            sb.append(ss);
        }
        return sb.toString();
    }
}
165.比较版本号

参考了加法模板,对两个字符串中的数字进行遍历比较,不够的补0.

class Solution {
    public int compareVersion(String version1, String version2) {
        String[]v1=version1.split("\\.");
        String[]v2=version2.split("\\.");
        int len1=v1.length;
        int len2=v2.length;
        int i=0;
        int j=0;
        while(i<len1||j<len2){
            int a=i<len1?Integer.valueOf(v1[i]):0;
            int b=j<len2?Integer.valueOf(v2[j]):0;
            if(a==b){
                i++;
                j++;
                continue;
            }
            return Integer.compare(a,b);
        }
        return 0;
    }
}
809.情感丰富的文字

参考了比较模板,这题的思路主要是两条字符串都从前往后遍历,将没法扩展的情况直接return false。至于能否拓展,主要还是看两者的字符以及对应的数量,比较好的实现方式是,直接把每个字符串中相同字符的数量也统计出来,从而判断拓展性。

class Solution {
    public int expressiveWords(String s, String[] words) {
        if(s.length()==0||words.length==0) return 0;
        int cnt=0;
        for(String w:words){
            if(isStrechy(s,w)) cnt++;
        }
        return cnt;
    }

    //判断b能否拓展成a
    public boolean isStrechy(String a,String b){
        int len1=a.length();
        int len2=b.length();
        int i=0;
        int j=0;
        while(i<len1&&j<len2){
            char c1=a.charAt(i);
            char c2=b.charAt(j);
            int cnt1=0;
            int cnt2=0;
            while(i<len1&&a.charAt(i)==c1){
                i++;
                cnt1++;
            }
            while(j<len2&&b.charAt(j)==c2){
                j++;
                cnt2++;
            }
            if(c1!=c2||cnt1<cnt2||cnt1!=cnt2&&cnt1<3) return false;
        }
        //当一个字符串中出现另一个字符串中没有出现的字符时,也没法扩张
        return i==len1&&j==len2;
    }
}
640.求解方程

总的来说就是要模拟移项的过程,将算式转换为factor*x=value的形式求解,其中主要是对x的系数factor以及value的求解,充分运用str.split()

class Solution {
    public String solveEquation(String equation) {
        String[] eq=equation.split("=");
        String[] eql=eq[0].replace("-","+-").split("\\+");
        String[] eqr=eq[1].replace("-","+-").split("\\+");
        int factor=0;
        int value=0;
        for(String s:eql){
            if("x".equals(s)){
                factor+=1;
            }else if("-x".equals(s)){
                factor+=-1;
            }else if(s.contains("x")){
                //正负数都加上
                factor+=Integer.parseInt(s.substring(0,s.length()-1));
            }else if(!s.equals("")){
                value-=Integer.parseInt(s);
            }
        }
        for(String s:eqr){
            if("x".equals(s)){
                factor-=1;
            }else if("-x".equals(s)){
                factor-=-1;
            }else if(s.contains("x")){
                //正负数都加上
                factor-=Integer.parseInt(s.substring(0,s.length()-1));
            }else if(!s.equals("")){
                value+=Integer.parseInt(s);
            }
        }
        if(factor==0){
            if(value==0) return "Infinite solutions";
            else return "No solution";
        }else{
            return "x="+value/factor;
        }
    }
}
1417.重新格式化字符串

分别统计出现的字母和数字,差值<=1的情况下,从数量较大的那种字符开始放起。

class Solution {
    public String reformat(String s) {
        Deque<Character> cq=new ArrayDeque<>(), numq=new ArrayDeque<>();
        int len=s.length();
        for(int i=0;i<len;i++){
            char c=s.charAt(i);
            if(c>='0'&&c<='9') numq.offer(c);
            else cq.offer(c);
        }
        int cqSize=cq.size();
        int numqSize=numq.size();
        if(Math.abs(cqSize-numqSize)>1) return "";
        StringBuilder sb=new StringBuilder();
        if(cqSize>=numqSize){
            for(int i=0;i<len;i++){
                if(i%2==0) sb.append(cq.poll());
                else sb.append(numq.poll());
            }
        }else{
            for(int i=0;i<len;i++){
                if(i%2==0) sb.append(numq.poll());
                else sb.append(cq.poll());
            }
        }
        return sb.toString();
    }
}
828.统计子串中的唯一字符

模拟+乘法原理应用题,原问题为求所有子数组的唯一字符数量和,其可等价为求每个 s[i]对答案的贡献,即每个 s[i] 可作为多少个子数组的唯一元素,这是乘法原理运用的地方。
参考宫水三叶的题解,其中两数组lr统计不同字符出现的左右边界(包含出现的相同字符)方法值得学习。cnts数组记录最近的左右边界,能够在O(1)的时间内找到相同的值出现的最近边界。

class Solution {
    public int uniqueLetterString(String s) {
        int len=s.length();
        //s中每个字母左右两边出现相同字符的位置
        int[]l=new int[len];
        int[]r=new int[len];
        //cnts用于统计从左到右出现某个字符的最近左边界,默认-1
        int[] cnts=new int[26];
        Arrays.fill(cnts,-1);
        for(int i=0;i<len;i++){
            int gap=s.charAt(i)-'A';
            l[i]=cnts[gap];
            cnts[gap]=i;
        }
        //cnts用于统计从右到左出现某个字符的最近右边界,默认len
        Arrays.fill(cnts,len);
        for(int i=len-1;i>=0;i--){
            int gap=s.charAt(i)-'A';
            r[i]=cnts[gap];
            cnts[gap]=i;
        }
        int ans=0;
        for(int i=0;i<len;i++){
            ans+=(i-l[i])*(r[i]-i);
        }
        return ans;
    }
}
1694.重新格式化电话号码

以4个字符作为分割标准。

class Solution {
    public String reformatNumber(String number) {
        StringBuilder sb=new StringBuilder();
        for(char c:number.toCharArray()){
            if(Character.isDigit(c)){
                sb.append(c);
            }
        }
        String s=sb.toString();
        int len=s.length();
        int cur=0;

        StringBuilder res=new StringBuilder();
        //剩余大于4个,放入3个
        while(len-cur>4){
            res.append(s.substring(cur,cur+3));
            res.append('-');
            cur+=3;
        }
        //等于4个的时候,每2个放入
        if(len-cur==4){
            res.append(s.substring(cur,cur+2));
            res.append('-');
            cur+=2;
            res.append(s.substring(cur,cur+2));
            cur+=2;
        }
        //小于4个,直接放
        if(len-cur<4){
            res.append(s.substring(cur,len));
        }
        return res.toString();
    }
}
1784.检查二进制字符串字段

统计有间隔的连续1,就是看第一个1以及后面是否会有0分割的1

class Solution:
    def checkOnesSegment(self, s: str) -> bool:
        #统计间隔的连续1
        gen=(s[i]=='1' and (i==0 or s[i-1]=='0') for i in range(len(s)))
        return sum(gen)<2
811.子域名访问计数

采用Python的字典做哈希,统计每个后缀出现的次数,其中用域名中的"."做分割值得学习,需要取当前点后domain[i+1:]当后缀。

class Solution:
    def subdomainVisits(self, cpdomains: List[str]) -> List[str]:
        # 用字典存储各个域名出现的次数
        # 从前往后找各个后缀的域名
        dic = defaultdict(int)
        for s in cpdomains:
            temp= s.split(' ')
            time,domain=int(temp[0]),temp[1]
            # 将当前完整的域名加入
            dic[domain]+=time
            for i in range(len(domain)):
                if domain[i]=='.':
                    dic[domain[i+1:]]+=time
        return [str(dic[c])+" "+c for c in dic.keys()]
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

互联网民工蒋大钊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值