数据结构与算法之字符串处理专题

基础知识

Java中提供两种实现字符串的类,分别是String、StringBuffer类。

String
字符串的声明:
1、常量声明方式
String sName=“Bob”;
2、对象声明方式
String sName=new String(“Bob”);
3、由字符数组初始化
String sName=new String(char[] ch);
4、由字符数组指定元素个数
String sName=new String(char[] ch,int index,int length);
4、指定字符数组的起始位置
String sName=new String(char[] ch,int begin,int end);

**注意:**String字符串是常量,一旦被创建就不能改变,这是因为字符串的值是存放在方法区的常量池里面,但是引用可以改变。

StringBuffer

StringBuffer对象的字符串长度是可变的,尽管用String定义的常量可以用函数去操作,但是会产生更多的中间变量,所以还是不宜使用,对于要经常修改的字符串,我们可以定义成StringBuffer类型的,方便修改使用。StringBuffer 上的主要操作是 append 和 insert 方法,可重载这些方法,以接受任意类型的数据。每个方法都能有效地将给定的数据转换成字符串,然后将该字符串的字符追加或插入到字符串缓冲区中。append 方法始终将这些字符添加到缓冲区的末端;而 insert 方法则在指定的点添加字符。
构造方法

StringBuffer()  
          构造一个其中不带字符的字符串缓冲区,其初始容量为 16 个字符。 
StringBuffer(CharSequence seq)  
          public java.lang.StringBuilder(CharSequence seq) 构造一个字符串缓冲区,它包含与指定的 CharSequence 相同的字符。 
StringBuffer(int capacity)  
          构造一个不带字符,但具有指定初始容量的字符串缓冲区。 
StringBuffer(String str)  
          构造一个字符串缓冲区,并将其内容初始化为指定的字符串内容。 

StringBuffer常用方法:

1append() 追加,可以接受任何类型数据。 
2insert()插入。 
3int capacity()返回当前容量 
4char charAt(int index) 返回指定位置的字符。 
5StringBuffer delete(int start,int end) 删除指定的字符串 
6void ensureCapacity(int minimumCapacity) 确保容量至少等于指定的最小值
7int indexOf(String str) 返回第一次出现的指定子字符串在该字符串中的索引
8int indexOf(String str, int fromIndex) 从指定的索引处开始,返回第一次出现的指定子字符串在该字符串中的索引 
9int lastIndexOf(String str) 返回最右边出现的指定子字符串在此字符串中的索引
10int lastIndexOf(String str, int fromIndex) 返回最后一次出现的指定子字符串在此字符串中的索引 
11void setCharAt(int index, char ch) 将给定索引处的字符设置为 ch 
12StringBuffer reverse() 将此字符序列用其反转形式取代。 
13String substring(int start) 返回一个新的 String,它包含此字符序列当前所包含的字符子序列。 

注意:String对象分配内存根据实际字符串长度,而StringBuffer分配内存时多开辟16个缓冲内存,所以使用capacity获取的为实际内存+16,而求字符串长度length()时,返回字符串长度。

StringBuilder
执行速度:StringBuilder > StringBuffer > String

stringBuffer 与 stringBuilder都是字符串变量,是可改变的对象,对字符串操作时,是在一个对象上进行操作的,不像string一样不断的创建对象来操作,所以速度快了,

stringBuilder是线程非安全的,stringBuffer是线程安全的,所以StringBuilder > StringBuffer

因此大多数情况下建议是用stringbuilder

使用总结:
1.如果要操作少量的数量:——>string
2.单线程操作字符串下操作大量数据——>stringBuilder
3.多线程操作字符串下操作大量数据 ——>stringBuffer

参考博客
参考博客

字符串拼接

可以用 + 直接拼接String,但由于String是不可修改对象,因此需要创建新的String对象,效率较慢。因此,不建议在for循环中使用”+”号操作符进行字符串拼接。

备注:在Java中,唯一被重载的运算符就是字符串的拼接相关的+,+=,除此之外,Java设计者不允许重载其他的运算符。

参考博客
参考博客

解题技巧

  1. 字符串题目务必细心呀!!!

题目练习

38外观数列

题目链接

给定一个正整数 n ,输出外观数列的第 n 项。

「外观数列」是一个整数序列,从数字 1 开始,序列中的每一项都是对前一项的描述。

你可以将其视作是由递归公式定义的数字字符串序列:
countAndSay(1) = “1”
countAndSay(n) 是对 countAndSay(n-1) 的描述,然后转换成另一个数字字符串。
外观数列如下:

1.     1
2.     11
3.     21
4.     1211
5.     111221
6. 	312211
7. 	13112221
8. 
第一项是数字 1 
描述前一项,这个数是 1 即 “ 一 个 1 ”,记作 "11"
描述前一项,这个数是 11 即 “ 二 个 1 ” ,记作 "21"
描述前一项,这个数是 21 即 “ 一 个 2 + 一 个 1 ” ,记作 "1211"
描述前一项,这个数是 1211 即 “ 一 个 1 + 一 个 2 + 二 个 1 ” ,记作 "111221"

要 描述 一个数字字符串,首先要将字符串分割为 最小 数量的组,每个组都由连续的最多 相同字符 组成。然后对于每个组,先描述字符的数量,然后描述字符,形成一个描述组。要将描述转换为数字字符串,先将每组中的字符数量用数字替换,再将所有描述组连接起来。
在这里插入图片描述

题目分析

本题要看懂题意,重点如下:
「外观数列」是一个整数序列,从数字 1 开始,序列中的每一项都是对前一项的描述
你可以将其视作是由递归公式定义的数字字符串序列:
countAndSay(1) = “1”
countAndSay(n) 是对 countAndSay(n-1) 的描述,然后转换成另一个数字字符串。

可以看出所谓外观数列就是从1开始,之后每一项对应的字符串中两两一对,描述前一项中的连续相同字符(个数+字符)

代码实现
class Solution {
    //遍历生成 时间复杂度O(N*M)N是给定的n,M是字符串最大长度 空间复杂度为O(N)
    public String countAndSay(int n) {
        //外观数列从数字1开始
        String str = "1";
        //依次生成外观数列的n项
        for(int i = 1; i < n; i++){
            //String是常量,创建后不能修改,只能指向新的引用,因此使用StringBuilder
            StringBuilder ns = new StringBuilder();
            //遍历str每个字符,生成新项
            for(int j = 0; j < str.length(); j++){
                //获取连续相同字符的数量
                int k = j;
                while(k < str.length() && str.charAt(k) == str.charAt(j)){
                    k++;
                }
                //新项中两两一对,描述前一项中的连续相同字符 个数+字符
                ns.append(Integer.toString(k - j)).append(str.charAt(j));
                //更新j到重复字符的最后一位,j++,就访问到新字符了
                j = k - 1;  
            }
            //转为字符串,继续生成下一项
            str = ns.toString();
        }

        return str;
    }
}

49. 字母异位词分组

题目链接

给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。

字母异位词 是由重新排列源单词的字母得到的一个新单词,所有源单词中的字母通常恰好只用一次。

在这里插入图片描述

题目分析

判断字母异位词应想到哈希表

本题也是采用哈希表,
先将各字母异位词的各字符按大小排序作为key,各字母异位词放在list中作为value,实现分组

时间复杂度时O(nmlogm) n是单词个数,m是单词最大长度 空间复杂度是O(nm)

代码实现
class Solution {
    //判断字母异位词应想到哈希表  
    //本题也是采用哈希表,先将各字母异位词的各字符按大小排序作为key,各字母异位词放在list中作为value,实现分组
    //时间复杂度时O(nmlogm) n是单词个数,m是单词最大长度 空间复杂度是O(nm)
    public List<List<String>> groupAnagrams(String[] strs) {
        Map<String, List<String>> map = new HashMap<>();
        //
        for(String str : strs){
            //对单词内字母排序
            char[] wordArray = str.toCharArray();
            Arrays.sort(wordArray);       //O(mlogm)
            //获取对应的键值
            String key = new String(wordArray);
            List<String> wordList = map.getOrDefault(key, new ArrayList<String>());
            wordList.add(str);
            //更新map
            map.put(key, wordList);  //O(1)
        }
        //返回字母异位词列表
        return new ArrayList<List<String>>(map.values());
    }
}

备注:
下列代码犯基础错误:

List<List<String>> result = new ArrayList<>();
result = map.values();

map.values返回的是java.util.Collection<java.util.List<java.lang.String>>
List<List>对应的是java.util.List<java.util.List<java.lang.String>>

也就是说List是collection的子类接口,子类引用不能指向父类对象,只能父类引用指向子类对象。
正确写法是:

List<List<String>> result = new ArrayList<>(map.values());

151. 翻转字符串里的单词

题目链接

给你一个字符串 s ,逐个翻转字符串中的所有 单词 。

单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。

请你返回一个翻转 s 中单词顺序并用单个空格相连的字符串。

说明:

输入字符串 s 可以在前面、后面或者单词间包含多余的空格。
翻转后单词间应当仅用一个空格分隔。
翻转后的字符串中不应包含额外的空格。
在这里插入图片描述

题目分析

两次翻转+去除多余的空格:具体代码可见官方题解
两次翻转如下:
“the sky is blue " =翻转整个字符串=> " eulb si yks eht” " =翻转各单词=> “blue is sky the”

这里采用的是两次翻转+移位,时间上会稍微快一点点。

代码实现
class Solution {
    //双翻转+移位,在原始数组上进行翻转。时间复杂度为O(n),空间复杂度O(1)
    public String reverseWords(String s) {
        //第一次翻转:字符串整体翻转  "the sky is blue " => " eulb si yks eht" 
        char[] initialArray = s.toCharArray();
        reverse(initialArray, 0, s.length() - 1);
        //控制翻转后的单词的各字符存入原数组的位置
        int k = 0;
        //翻转各个单词并存入原数组
        for(int i = 0; i < initialArray.length; i++){
            if(initialArray[i] == ' '){
                continue;
            }
            //记录单词起始位
            int cur = i;
            //获取单词终止位  退出循环时i指向空格或超出数组长度
            while(i < initialArray.length && initialArray[i] != ' '){
                i++;
            }
            //翻转单词并依次将各字符存入原数组
            for(int j = cur; j < i; j++){
                //翻转单词
                if(j == cur){
                    reverse(initialArray, cur, i - 1);
                }
                //翻转后的字符存入原数组对应位置 移位操作
                initialArray[k++] = initialArray[j];
                //单词后加空格隔开
                if(j == i - 1){
                    //避免越界情况,例如=> "asdasd df f",不加判断最后就会数组越界
                    //但k最后不一定等于length
                    //如翻转后字符串为"asd df f  ",此时得到"dsa fd f ",需要继续处理
                    if(k < initialArray.length){
                        initialArray[k++] = ' ';
                    }
                }
            }

        }
        //如果k=0说明输入字符串为空字符串
        if(k == 0){
            return "";
        }else{
            //若最后1个字符为空格()应去除(终止位k-1) 如上述翻转后字符串为"asd df f  "得到"dsa fd f "
            return new String(initialArray, 0, (initialArray[k - 1] != ' ') ? k : k - 1);
        }

    }

    
    public void reverse(char[] chars, int begin, int end){
        char temp;
        for(int i = begin, j = end; i < j; i++,j--){
            temp = chars[i];
            chars[i] = chars[j];
            chars[j] = temp;
        }
    }
    
}

165. 比较版本号

题目链接

给你两个版本号 version1 和 version2 ,请你比较它们。

版本号由一个或多个修订号组成,各修订号由一个 ‘.’ 连接。每个修订号由 多位数字 组成,可能包含 前导零 。每个版本号至少包含一个字符。修订号从左到右编号,下标从 0 开始,最左边的修订号下标为 0 ,下一个修订号下标为 1 ,以此类推。例如,2.5.33 和 0.1 都是有效的版本号。

比较版本号时,请按从左到右的顺序依次比较它们的修订号。比较修订号时,只需比较 忽略任何前导零后的整数值 。也就是说,修订号 1 和修订号 001 相等 。如果版本号没有指定某个下标处的修订号,则该修订号视为 0 。例如,版本 1.0 小于版本 1.1 ,因为它们下标为 0 的修订号相同,而下标为 1 的修订号分别为 0 和 1 ,0 < 1 。

返回规则如下:

如果 version1 > version2 返回 1,
如果 version1 < version2 返回 -1,
除此之外返回 0。
在这里插入图片描述

题目分析

双指针法

代码实现
class Solution {
    public int compareVersion(String version1, String version2) {
        int m = version1.length();
        int n = version2.length();
        
        int i = 0, j = 0;
        int x, y;
        while(i < m || j < n){
            x = 0;
            for(; i < m && version1.charAt(i) != '.'; ++i){
                x = x * 10 + version1.charAt(i) - '0'; 
            }
            //跳过'.'
            i++;
            y = 0;
            for(; j < n && version2.charAt(j) != '.'; j++){
                y = y * 10 + version2.charAt(j) - '0';
            }
            j++;

            if(x != y){
                return x > y ? 1 : -1;
            }
        }
        return 0;
    }
}

929. 独特的电子邮件地址

题目链接

每个 有效电子邮件地址 都由一个 本地名 和一个 域名 组成,以 ‘@’ 符号分隔。除小写字母之外,电子邮件地址还可以含有一个或多个 ‘.’ 或 ‘+’ 。

例如,在 alice@leetcode.com中, alice 是 本地名 ,而 leetcode.com 是 域名 。
如果在电子邮件地址的 本地名 部分中的某些字符之间添加句点(’.’),则发往那里的邮件将会转发到本地名中没有点的同一地址。请注意,此规则 不适用于域名 。

例如,"alice.z@leetcode.com” 和 “alicez@leetcode.com” 会转发到同一电子邮件地址。
如果在 本地名 中添加加号(’+’),则会忽略第一个加号后面的所有内容。这允许过滤某些电子邮件。同样,此规则 不适用于域名 。

例如 m.y+name@email.com 将转发到 my@email.com。
可以同时使用这两个规则。

给你一个字符串数组 emails,我们会向每个 emails[i] 发送一封电子邮件。返回实际收到邮件的不同地址数目

在这里插入图片描述

题目分析

name单独处理后再与domain拼接即可得到实际的邮箱地址;

借助哈希表HashSet存储实际地址,因为其特性是元素不可重复。

代码实现
class Solution {
    public int numUniqueEmails(String[] emails) {
        //哈希表存储实际邮箱地址  元素不可重复
        Set<String> hashSet = new HashSet();

        for(String email : emails){
            //注意indexOf(char xx) 参数为char
            int atIndex = email.indexOf('@');
            String name = email.substring(0, atIndex);
            String domain = email.substring(atIndex);
            //注意contains(String xx)参数为String
            if(name.contains("+")){
                name = name.substring(0, name.indexOf('+'));
            }
            //应该转义特定字符“.”,因为它被视为正则表达式。
            //注意replaceAll(String xx, String xx) 参数为String 返回替换后的String
            name = name.replaceAll("\\.", "");  

            hashSet.add(name + domain);
        }
        return hashSet.size();
    }
}

5. 最长回文子串

题目链接

给你一个字符串 s,找到 s 中最长的回文子串。
在这里插入图片描述

题目分析

中心扩展算法:定一个中心点,若其左右两边字符相等,则移动左右两指针,直到不等为止,如此便得到一回文串;

注意:初始时,左右指针都指向中心点;结束时左指针指向回文串起始位前1位,右指针指向终止位后1位,因此需要计算该回文串长度时要减1

代码实现

class Solution {
    //中心扩展算法 时间复杂度O(n^2)  空间复杂度O(1) 证明见官方
    public String longestPalindrome(String s) {
        if(s == null || s.length() < 1){
            return "";
        }
        //记录回文串始终位置   处于节省空间考虑,因为java中String为常量,若定义字符串记录结果会浪费
        int resStart = 0, resEnd = 0;
        //遍历字符串 求得最大长度回文串
        for(int i = 0; i < s.length(); i++){
            //需要考虑回文串长度为奇数和偶数两种情况(acbca  acbbca)
            int len1 = expandAroundCenter(s, i, i);  //奇数  中心点只有1个
            int len2 = expandAroundCenter(s, i, i + 1); //偶数 中心点有两个
            //如果回文串存在且中心点有两个,则len2一定大于len1,否则len2<len1,说明中心点只有1个
            int len = Math.max(len1, len2);
            //记录最长回文串
            //这里写成len>resEnd-resStart也可以是因为回文串是左右两边成对,多3和多2都是多  
            if(len > resEnd - resStart){
                //起始位应为中心点-len/2,考虑到中心点可能有两个,因此需要向下取整(len - 1/2)
                resStart = i - (len - 1) / 2;
                //终止位应为中心点+len/2
                resEnd = i + len / 2;
            }
        }
         //substring(start,end)是取左闭右开区间
        return s.substring(resStart, resEnd + 1);
    }

    //设定一个中心点,若其左右两边字符相等,则移动左右两指针,直到不等为止,如此便得到一回文串
    //初始时,左右指针都指向中心点;
    public int expandAroundCenter(String s, int left, int right){
        while(left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)){
            --left;
            ++right;
        }
        //返回该回文串的长度 退出循环时left指向回文串起始位前1位,right指向终止位后1位,因此需要-1
        return right - left - 1;
    }
}

6. Z 字形变换

题目链接

将一个给定字符串 s 根据给定的行数 numRows ,以从上往下、从左到右进行 Z 字形排列。
在这里插入图片描述
在这里插入图片描述

题目分析

如下图,z字形对应的字符下标,观察下标找规律(设行数n=5)
第1行和最后一行的下标是公差为step=2(n-1)的等差数列;
其他行索引就是两个等差数列交错进,第一个首项为行号,公差为step,第二个首项为step-行号,公差为step。
在这里插入图片描述

代码实现
class Solution {
    //按行访问  时间复杂度O(n) 空间复杂度O(n)起始下标都是行号
    public String convert(String s, int numRows) {
        if(numRows == 1){
            return s;
        }

        StringBuilder result = new StringBuilder();
        int len = s.length();
        int step = 2 * (numRows - 1);

        for(int i = 0; i < numRows; i++){
            //第1行和最后一行的下标是公差为2(n-1)的等差数列
            if(i == 0 || i == numRows -1){
                for(int j = i; j < len; j += step){
                    result.append(s.charAt(j));
                }
            }else{
                //其他行索引就是两个等差数列交错进行,
                //第一个首项为行号,公差j=step;第二个首项为step-行号,公差k=step
                for(int j = i, k = step - i; j < len || k < len; j += step, k += step){
                    if(j < len) result.append(s.charAt(j));
                    if(k < len) result.append(s.charAt(k));
                }
            }
        }
        
        return result.toString();
    }
}

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

题目链接

给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。
在这里插入图片描述

题目分析

滑动窗口+哈希集合

代码实现
class Solution {
    //滑动窗口+哈希集合  时间复杂度为O(n)
    public int lengthOfLongestSubstring(String s) {
        //哈希集合,记录每个字符是否出现过
        Set<Character> hashSet = new HashSet<>();
        int n = s.length();
        //右指针,初始时指向字符串首字符前一位
        int right = -1, ans = 0;
        //实现滑动窗口,窗口中始终为不重复的字符
        for(int i = 0; i < n; i++){
            if(i != 0){
                //初始时,遇到重复字符i才会大于0,此时窗口右移一位
                //左指针向右移动一格,移除之前指向的字符
                hashSet.remove(s.charAt(i - 1));
            }
            //右指针移动,遇到重复字符则退出循环
            while(right + 1 < n && !hashSet.contains(s.charAt(right + 1))){
                hashSet.add(s.charAt(right + 1));
                right++;
            }
            //记录当前最大长度无重复字符串的长度
            ans = Math.max(ans, right - i + 1);
        }
        return ans;
    }
}

208. 实现 Trie (前缀树)

题目链接

Trie(发音类似 “try”)或者说 前缀树 是一种树形数据结构,用于高效地存储和检索字符串数据集中的键。这一数据结构有相当多的应用情景,例如自动补完和拼写检查。

请你实现 Trie 类:

Trie() 初始化前缀树对象。
void insert(String word) 向前缀树中插入字符串 word 。
boolean search(String word) 如果字符串 word 在前缀树中,返回 true(即,在检索之前已经插入);否则,返回 false 。
boolean startsWith(String prefix) 如果之前已经插入的字符串 word 的前缀之一为 prefix ,返回 true ;否则,返回 false 。

在这里插入图片描述

题目分析

Trie,又称前缀树或字典树或单词查找树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。

基本性质:
根节点不包含字符,除根节点外每一个节点都只包含一个字符;
从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串;
每个节点的所有子节点包含的字符都不相同;
每个节点包含布尔字段 isEnd,表示该节点是否为字符串的结尾。

在这里插入图片描述
在字典树上搜索添加过的单词的步骤如下:
1、从根结点开始搜索
2、取得要查找单词的第一个字母,并根据该字母选择对应的字符路径向下继续搜索。
3、字符路径指向的第二层节点上,根据第二个字母选择对应的字符路径向下继续搜索。
4、一直向下搜索,如果单词搜索完后,找到的最后一个节点是一个终止节点,比如图1中的实心节点,说明字典树中含有这个单词,
如果找到的最后一个节点不是一个终止节点,说明单词不是字典树中添加过的单词。如果单词没搜索完,但是已经没有后续的节点了,也说明单词不是字典树中添加过的单词。

参考博客

代码实现
class Trie {
    //每个节点包含指向子节点的数组children和布尔字段isEnd。
    private Trie[] children;    //本题中数组长度为小写英文字母的数量26
    private boolean isEnd;     //表示当前节点是否为字符串的结尾

    //初始化前缀树对象
    public Trie() {
        children = new Trie[26];
        isEnd = false;
    }
    
    //向前缀树中插入字符串word 
    public void insert(String word) {
        Trie node = this;
        for (int i = 0; i < word.length(); i++) {
            char ch = word.charAt(i);
            int index = ch - 'a';
            //在数组对应位置存放新节点引用地址,从而表明该路径的字母
            if (node.children[index] == null) {
                node.children[index] = new Trie();
            }
            //继续向下迭代 
            node = node.children[index];
        }
        node.isEnd = true;
    }

    //如果字符串word在前缀树中,返回true
    public boolean search(String word) {
        Trie node = searchPrefix(word);
        return node != null && node.isEnd;
    }
    
    //如果之前已经插入的字符串word的前缀之一为prefix,返回true
    public boolean startsWith(String prefix) {
        return searchPrefix(prefix) != null;
    }
    
    private Trie searchPrefix(String prefix) {
        Trie node = this;
        for (int i = 0; i < prefix.length(); i++) {
            char ch = prefix.charAt(i);
            int index = ch - 'a';
            //判断对应字符所在位置是否为空以确定是否包含该字符
            if (node.children[index] == null) {
                return null;
            }
            node = node.children[index];
        }
        return node;
    }
}

273. 整数转换英文表示

题目链接

将非负整数 num 转换为其对应的英文表示。

在这里插入图片描述

题目分析

代码实现


class Solution {
    String[] singles = {"", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine"};
    String[] teens = {"Ten", "Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen"};
    String[] tens = {"", "Ten", "Twenty", "Thirty", "Forty", "Fifty", "Sixty", "Seventy", "Eighty", "Ninety"};
    String[] thousands = {"", "Thousand", "Million", "Billion"};

    public String numberToWords(int num) {
        if (num == 0) {
            return "Zero";
        }
        StringBuffer sb = new StringBuffer();
        for (int i = 3, unit = 1000000000; i >= 0; i--, unit /= 1000) {
            int curNum = num / unit;
            if (curNum != 0) {
                num -= curNum * unit;
                StringBuffer curr = new StringBuffer();
                recursion(curr, curNum);
                curr.append(thousands[i]).append(" ");
                sb.append(curr);
            }
        }
        return sb.toString().trim();
    }

    public void recursion(StringBuffer curr, int num) {
        if (num == 0) {
            return;
        } else if (num < 10) {
            curr.append(singles[num]).append(" ");
        } else if (num < 20) {
            curr.append(teens[num - 10]).append(" ");
        } else if (num < 100) {
            curr.append(tens[num / 10]).append(" ");
            recursion(curr, num % 10);
        } else {
            curr.append(singles[num / 100]).append(" Hundred ");
            recursion(curr, num % 100);
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值