把字符串转换成整数(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;
}