把字符串转换成整数(atoi):最直观的想法是,从字符串高位向低位遍历,然后逐个取字符串元素,先将字符转换为整型,再利用数位关系将其转换为数值。在纯数字的情况下,当然可以像这样做,但是本题中含有非法字符,且明确说明,在若干空格后,可能会存在符号,接着出现的一连串数字才是转换为数值的重要部分,如果后面再出现非法字符需要被舍弃。也就是说,字符串1234hello转换为数字则为1234,而字符串hello1234转换为数字是为0,即其是非法的,那么我们就不能从后向前遍历字符串,而应该从前向后遍历字符串。(试错)
idea:使用index标记字符串下标,其初始化为0;使用flag标记正负数,其true表示正数而false表示负数;使用hasNum标记是否已经碰到过数字,其true表示碰到过而false表示没碰到;使用res标记结果,其初始为0;具体做法如下:首先去掉前导空格,然后标记正负数,接着遍历剩余字符:如果遇到空格,则判断是否碰到过数字,如果碰到过则直接break,因为题目中说明是符号后一连串的整数,例如字符串+0 1234,其转换为数字应该是0,反之则直接跳过空格;如果遇到数字,则标记碰到过数字,然后进行越界处理,接着如果没越界则继续计算数值;如果遇到其他非法字符,则直接break;最后根据flag标记的正负数情况来处理结果res,再返回res即可。
// 处理越界 可以学习一下越界处理 if(res>INT_MAX/10||(res==INT_MAX/10 &&(s[index]-'0')>INT_MAX%10)) return flag==true?INT_MAX:INT_MIN;
int StrToInt(string s) { // 字符串长度 int n=s.size(); // 字符串下标 int index=0; // 标记正负数 true是正数 false是负数 bool flag=true; // 标记记录过数字 bool hasNum=false; // 记录结果 int res=0; // 去除前导空格 while(s[index]==' ') index++; // 标记负数 if(s[index]=='-') { flag=false; index++; } // 正数 默认也是正数 else if(s[index]=='+') { index++; } // 遍历剩余字符 while(index<n) { // 去除空格 if(s[index]==' ') { // + 0 1234 if(hasNum) break; else index++; } // 数字 0-9 else if(s[index]>='0'&&s[index]<='9') { // 标记出现数字 hasNum=true; // 处理越界 if(res>INT_MAX/10||(res==INT_MAX/10 &&(s[index]-'0')>INT_MAX%10)) return flag==true?INT_MAX:INT_MIN; res=res*10+(s[index]-'0'); index++; } // 非法字符 else break; } // 负数 if(flag==false) res=-res; return res; }
优化:状态机模型。我们知道,根据上述转换规则来写的话,代码会出现很多判断条件,从而显得十分臃肿。其实,一般涉及到字符串的转换时,可以考虑状态机的解法。有限状态机指的是有限个状态以及这些状态之间的转移,其中转移是由当前状态以及条件得到下一个状态,所以我们首先要考虑有哪几个状态以及有哪些条件,再根据状态个数m和条件个数n绘制出大小为m*n的状态转移矩阵。以该题为例:字符类型无非就是空格、符号+/-、数字0-9、以及其他非法字符;状态无非就是初始状态start、符号位sign、数值number、以及结束end;其状态转移图以及状态转移表如下。
start | start | sign | number | end |
sign | sign | end | number | end |
number | end | end | number | end |
end | end | end | end | end |
idea:我们可以将上述四个状态映射为数字0、1、2、3,然后使用一个常数矩阵来存储状态转移,接着从头到尾遍历字符串,根据当前的字符类型,进入相应的状态,同时判断是否超过int上下界。
// 其中行表示当前状态 列分别表示遇到空格、符号、数字、非法字符相应的转换状态 vector<vector<int>> states={ {0,1,2,3}, {1,3,2,3}, {3,3,2,3}, {3,3,3,3} // 遇到非法字符直接退出循环故该行可省略 };
int StrToInt(string s) { // 其中行表示当前状态 列分别表示遇到空格、符号、数字、非法字符相应的转换状态 vector<vector<int>> states={ {0,1,2,3}, {1,3,2,3}, {3,3,2,3} }; // 存储结果 int res=0; // 标记正负数 bool flag=true; // 初始状态为0 int state=0; // 字符串长度 int n=s.size(); // 字符串当前元素 char ch; // 从前向后遍历字符串 for(int i=0;i<n;i++) { // 当前元素 ch=s[i]; // 遇到空格 if(ch==' ') state=states[state][0]; // 遇到符号 else if(ch=='+'||ch=='-') { state=states[state][1]; // 第一次遇到符号 if(state==1) flag=(ch=='-')?false:true; // 第二次遇到符号 else break; } // 遇到数字 else if(ch>='0'&&ch<='9') state=states[state][2]; // 遇到非法字符 else break; // 非法状态 if(state==3) break; // 计算数值 if(state==2) { // 溢出处理 if(res>INT_MAX/10||(res==INT_MAX/10 &&(ch-'0')>INT_MAX%10)) return flag==true?INT_MAX:INT_MIN; // 计算数值 res=res*10+(ch-'0'); } } // 根据flag返回结果 return flag==true?res:-res; }