8. 字符串转换整数 (atoi)

题目描述

截图自官方

 

代码

自己写的失败代码本题答的丑陋得不忍直视,先想着如果第一个为+,-或一个数字就进入if,然后从第二个开始判断,但这样如果样例只有一个数字,转long那里直接转换异常,补充了第一次改进的代码。又发现+-2这种会得到"+",转换会出错,又进行了第二次改进。但是这样发现"3.2"这种会返回0,进行第三次改进,第三次改进这里最重要的问题就是String类型的相等与否判断,这是个细节。改正后样例“20000000000000000000”这种又没办法解决了,不能用大数库。放弃自己解了。

class Solution {
    // 本题答的丑陋得不忍直视,先想着如果第一个为+,-或一个数字就进入if,然后从第二个字符开始判断,但这样如果样例只有一个数字,转long那里直接转换异常,补充了第一次改进的代码。又发现+-2这种会得到"+",转换会出错,又进行了第二次改进。但是这样发现"3.2"这种会返回0,进行第三次改进,第三次改进这里最重要的问题就是String类型的相等与否判断,这是个细节。改正后样例“20000000000000000000”这种又没办法解决了。放弃自己解了。最后一个测试用例"-9223372036854775809"过不了。这种解法根本不通
    // 语法细节:String类型的相等与否判断
    public int myAtoi(String str) {
        // 确实可以传入null,引发NullPointerException
        if(str==null){
            return 0;
        }
        // 去掉空格,"   "会变成""
      String s1=str.trim();
     if(s1.length()==0){
         return 0;
     }
     if(s1.charAt(0)=='+'||s1.charAt(0)=='-'||(s1.charAt(0)>='0'&&s1.charAt(0)<='9')){
        //  第一次改进,解决只传入一位数字的情况
        if(s1.length()==1){
            if(s1.charAt(0)=='+'||s1.charAt(0)=='-'){
                return 0;
            }else{
                return Integer.parseInt(s1);
            }
        }
        String temp="";
        // 这样的话输入个1位数1会报错,因为直接没进for,导致temp为空,导致res转换报异常NumberFormatException
        for(int i=1;i<s1.length();i++){
             if((s1.charAt(i)<'0'||s1.charAt(i)>'9')){
                temp=s1.substring(0,i);

                // 第二次改进:防止+-2这种,但是进来个3.5也返回0了,废弃,不能这样写
                // if(temp.length()==1){
                //     return 0;
                // }
      


         // 第三次改进,这里比较总为false,出错应该是string比较相等与否部分的问题。
                //  if(temp=="+"||temp=="-"){
                     if(temp.equals("+")||temp.equals("-")){
                    return 0;
                }
                break;
             }
             temp=s1;
        }
        try{
        long res=Long.parseLong(temp);
        if(res<Integer.MIN_VALUE){
            return Integer.MIN_VALUE;
        }
        if(res>Integer.MAX_VALUE){
            return Integer.MAX_VALUE;
        }
        return (int)res;
        }catch(Exception e){
            return Integer.MAX_VALUE;
            // 不能直接用,得导包
            
        //   BigInteger a = new BigInteger(temp);
//                BigInteger b=BigInteger.valueOf(Integer.MAX_VALUE);
                // 妄图通过大数解决,失败。大数好像不可以比大小,编译不通过
//                 if(a.subtract(b)>0){
//                     return Integer.MAX_VALUE;
//                 }else return Integer.MIN_VALUE;
           }
        
    }else return 0;
    
    }
}

 

官方解法:

这是一道运用确定性有限状态自动机(deterministic finite automaton, DFA)的典型题目。

官方给出的解释图例如下:

 

 

 

 

注意代码中的提到的关于int的语法漏洞。

// 官方解法
// 注意int间的乘法,加法结果还为int,导致溢出的问题。只有int*long才会自动转型
class Solution {
    public int myAtoi(String str) {
        Automaton a=new Automaton();
        for(int i=0;i<str.length();i++){
            a.calculate(str.charAt(i));
        }
      // 这个,同样存在语言漏洞,神奇的是,当long型2147483648先转int,结果为-2147483648,而正数max过来没影响。导致最终结果歪打正着,但是本身程序有漏洞。
        // 原因:long型2147483648是000000000000100000000000(64位),你转为int,就按int的规则截断解读,刚好10000000000是Integer.MINVALUE的补码;乘-1后,结果还是-2147483648(乘法这里原理不知道,只知道结果)。
        // return a.sign*(int)a.ans;
        return (int)(a.sign*a.ans);
    }
}
// 其实写在一个一个类里也行,这里为了清楚地表示一下有限状态自动机的结构。
class Automaton{
    int sign=1;
    long ans =0;
    Map<String,String[]> statec=new HashMap();
    // 设定开始状态
    String state="start";
// 可能接受到的输入类型:空白字符,+或-,数字,所有其他

    public Automaton() {
        statec.put("start", new String[]{"start", "signed", "innumber", "end"});
        statec.put("signed", new String[]{"end", "end", "innumber", "end"});
        statec.put("innumber", new String[]{"end", "end", "innumber", "end"});
        statec.put("end", new String[]{"end", "end", "end", "end"});
    }


    public void calculate(char a){
        int t=getOption(a);
        state=statec.get(state)[t];
        if("signed".equals(state)){
            if(a=='-'){
                sign=-1;
            }
        }
        if("innumber".equals(state)){
            // 在内部是想让ans始终为正,符号在最终返回结果那里处理
            ans=ans*10+a-'0';
            if(sign==1&&ans>Integer.MAX_VALUE){
                ans=Integer.MAX_VALUE;
            }else if((sign==-1)&&(ans*(-1)<Integer.MIN_VALUE)){
                // 第一次改进
                //  这一句的漏洞藏的很深,ans为long型,初衷本来是向让ans越过最小值时固定住他的值为正数(-1)*Integer.MIN_VALUE,然后在最终返回结果那里乘符号位(-1)就刚刚好,强转后刚好为Integer.MIN_VALUE。但是 ans=(-1)*Integer.MIN_VALUE;自己会在内部循环时就乘好多次-1,导致传到最终返回结果那里时ans=Integer.MIN_VALUE,在最终那里乘-1后变成了Integer.MAX_VALUE+1。强转int后直接溢出。导致大负数样例"-91283472332"结果出错。

            // 上面的分析不对。不是这个原因,因为ans写死了,不会内部乘-1.问题出在了(-1)*Integer.MIN_VALUE,两个int相乘,结果为int,溢出了,赋给ans的是个溢出后的int值。
                // ans=(-1)*Integer.MIN_VALUE;


                // 第二次修改,直接这样固定岂不美哉?  这样固定ans直接变为了Integer.MIN_VALUE,肯定有细节问题。-->还是这个问题,两个int相加结果为int,二进制1111111+1=1000000,刚好是Integer.MIN_VALUE的补码。溢出了,他们不会自动转long,只会截断解读为int。要改成int+long这样结果才自动为long。不会溢出
                // ans=Integer.MAX_VALUE+1;

                ans=Integer.MAX_VALUE+1l;
            }
        }
    }
    public int getOption(char a){
        if(a==' '){
            return 0;
        }else if(a=='+'||a=='-'){
            return 1;
        }else if(a-'0'>=0&&a-'0'<=9){
            return 2;
        }else return 3;
    }
}

 

语法漏洞记录

1.String型相等与否的比较

 

涉及到两个String比较要注意,一般为了比较字面是否相等用equal就没问题,比如刷题中第八题:

直接==比较就导致程序流出错。

// 第三次改进,这里比较总为false,出错应该是string比较相等与否部分的问题。        

//  if(temp=="+"||temp=="-"){

 

 

2.int型运算类型转换问题

        long a=Integer.MAX_VALUE+1l;/*2147483648*/
        long b=Integer.MAX_VALUE+1;/*-2147483648*/
        System.out.println((-1)*Integer.MIN_VALUE);/*-2147483648*/

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值