题目描述
截图自官方
代码
自己写的失败代码本题答的丑陋得不忍直视,先想着如果第一个为+,-或一个数字就进入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*/