zoukankan      html  css  js  c++  java
  • 微软面试题_中文字符串转换为数字

    微软面试题_中文字符串转换为数字

    Contents

    题目


    解答

    方法1:单调栈

    参考把中文表示的数字转成阿拉伯数字 - java

    1. 遍历一次字符串,判断字符串中是否包含单位,这两种情况下的处理逻辑是不同的
    2. 再遍历一次字符串,计算数字
    public class zhToNumber {
        public static void main(String[] args) {
            System.out.println(convert("八亿四千八百卅万零二千六百廿五"));
            System.out.println(convert("三零四五七八九"));
        }
    
        static String digit = "零一二三四五六七八九";
        static String unit = "十廿卅百千万亿";
        static int[] unitVal = new int[]{10, 20, 30, 100, 1000, 10000, 100000000};
    
        public static long convert(String zh) {
            long num = 0;
            boolean containUnit = false;
            Stack<Integer> stack = new Stack<>();
            char[] arr = zh.toCharArray();
            for (char c : arr) {
                if (unit.indexOf(c) != -1) containUnit = true;
            }
            //包含单位的情况,单调栈
            if (containUnit) {
                for (char cur : arr) {
                    if (cur == '零') continue;
                    int unitIdx = unit.indexOf(cur);
                    //如果当前字符是数字,直接将其入栈,等待之后遇到单位时,与单位相乘
                    if (unitIdx == -1) {
                        stack.push(digit.indexOf(cur));//这个方法用的非常妙!
                    }
                /*如果当前字符是单位,则需要将这个单位乘到修饰这个单位的数字上
                修饰的数字满足两个条件:比当前单位数值小;比当前单位先入栈。*/
                    else {
                        int curUnitVal = unitVal[unit.indexOf(cur)];//当前单位对应的值
                        int stackSum = 0;//stack当中修饰当前单位的数值求和
                        while (!stack.isEmpty() && stack.peek() < curUnitVal) {
                            stackSum += stack.pop();
                        }
                        if (stackSum == 0) {//没有修饰当前单位的数值,直接把当前单位的值入栈
                            stack.push(curUnitVal);
                        } else {
                            stack.push(curUnitVal * stackSum);
                        }
                    }
                }
                while (!stack.isEmpty()) {
                    num += stack.pop();
                }
                return num;
            } else {
                //不包含单位的情况,直接遍历数字
                for (char cur : arr) {
                    if (digit.indexOf(cur) != -1) {
                        int curVal = digit.indexOf(cur);
                        num = num * 10 + curVal;
                    }
                }
                return num;
            }
        }
    }

    复杂度分析

    时间复杂度:O(n),遍历两轮
    空间复杂度:O(n),需要用一个辅助栈保存临时结果

    方法2:递归

    递归的核心在于如何把整体问题分解为很多结构相似的子问题。
    本题中的整体问题是:求出整个字符串表示的数值。
    分解问题的思路是:

    1. 找出整个字符串中最大的单位maxUnit以及在字符串中的索引maxUnitIdx
    2. maxUnitIdx作为分界点,可以把整个字符串[lo...hi]划分为三个部分
      • beforeUnit区间为[lo...maxUnitIdx - 1],即单位之前的数值
      • maxUnit即当前区间[lo...hi]当中的最大单位的数值
      • afterUnit区间为[maxUnitIdx+1...hi]即单位之后的数值
    3. [lo...hi]区间的数值 = beforeUnit * maxUnit + afterUnit,其中的beforeUnitafterUnit就是子问题,需要进一步调用递归函数求解。

    至此,我们已经搞清楚如何分解问题,接下来需要进一步确定递归函数的一系列要素:

    • 递归函数签名(即输入输出)
      • 输入字符串,以及一个由左右边界表示的范围;输出这个范围内的字符串表示的值。
      • private static long recur(char[] arr, int lo, int hi)
    • 终止条件
      • 终止条件是lo == hi,也就是区间内只有一个字符,返回这个字符表示的数字。
      • 可以推断,如果输入是合法数值的话,这种区间内必然是一个表示数字的字符(因为单位不可能单独出现,必然会有单位前的一个系数,如果有单位的话,不可能在递归调用的区间内,因为递归调用都是把单位的索引排除在外的)。
    • 返回值
      • beforeUnit * maxUnit + afterUnitbeforeUnitafterUnit需要进一步调用递归函数求解。
      • 注意特殊的情况:beforeUnitafterUnit两个部分都可能是空的,二者是空区间对应的数值是不同的给,如果beforeUnit是空,对应1;如果afterUnit是空,对应0。

    最终,还有一类特殊情况需要处理,就是字符'零'的情况。在我们的实现中,如果出现'零'会导致无限递归,无法跳出递归。而事实上中文数字里的'零'是没有信息量,可以省去的(比如一百零一和一百一是一样的),所以在进入递归函数之前,先使用replace()方法把所有的'零'都去除掉。

    下面的代码中没有考虑不带单位的情况,是因为不带单位的情况跟方法1中写法一样,就不重复写了。

    public class zhToNumber_2 {
        public static void main(String[] args) {
            String test1 = "二亿三千四百五十万卅千零廿九";
            test1 = test1.replace("零", "");//去除所有的零
            char[] arr = test1.toCharArray();
            System.out.println(recur(arr, 0, arr.length - 1));
        }
    
        private static String digit = "零一二三四五六七八九";
        private static String unit = "十廿卅百千万亿";
        private static int[] unitVal = new int[]{10, 20, 30, 100, 1000, 10000, 100000000};
    
        private static long recur(char[] arr, int lo, int hi) {
            //递归终止条件:遇到只有一个字符的情况,并且这个字符表示数字,那么可以直接返回这个数字
            if (lo == hi && digit.indexOf(arr[lo]) != -1) {
                return digit.indexOf(arr[lo]);
            }
            //找出arr[lo...hi]中最大的单位,以及最大单位的索引
            int maxUnit = 0, maxUnitIdx = -1;
            for (int i = lo; i <= hi; i++) {
                char cur = arr[i];
                int curUnit = unit.indexOf(cur);
                if (curUnit != -1 && unitVal[curUnit] > maxUnit) {
                    maxUnit = unitVal[curUnit];
                    maxUnitIdx = i;
                }
            }
            //以最大的单位为分割点,将字符串分为三个部分,[beforeUnit][maxUnit][afterUnit],返回值为:beforeUnit * maxUnit + afterUnit
            //单位之前如果是空字符串,那么maxUnit应该乘以1
            long beforeUnit = lo > maxUnitIdx - 1 ? 1 : recur(arr, lo, maxUnitIdx - 1);
            //单位之后如果是空字符串,那么beforeUnit * maxUnit应该加上0
            long afterUnit = maxUnitIdx + 1 > hi ? 0 : recur(arr, maxUnitIdx + 1, hi);
            //返回值:arr[lo...hi]字符串表示的数值
            return beforeUnit * maxUnit + afterUnit;
        }
    }

    复杂度分析

    时间复杂度:O(m * n),m为递归调用次数,n为每次递归调用的区间长度的均值,因为每次递归调用都要遍历长度为n的字符数组
    空间复杂度:O(m * n),递归每次递归调用都会维护一个新的字符数组

  • 相关阅读:
    linux(ubuntu) 安装 node.js
    正反向代理
    js正则表达式----replace
    webpack开发小总结
    稳定排序和非稳定排序
    树状数组 --- (离散化+树状数组、求逆序对)
    编程之美初赛第一场 题目3 : 活动中心
    巴什博弈 杭电 1847
    巴什博弈 杭电 1846
    约瑟夫环问题详解
  • 原文地址:https://www.cnblogs.com/Howfars/p/14747859.html
Copyright © 2011-2022 走看看