string类常见题目详解

下面的题目是对string中的常见函数的使用,便于熟悉string类!

目录

题目一:把字符串转换成整数 (atoi)

题目二:字符串相加

题目三:反转字符串

题目四: 字符串中的第一个唯一字符

题目五:验证回文结构

题目六:字符串最后一个单词的长度

题目七:反转字符串Ⅱ(区间部分反转)

题目八:反转字符串中的单词

题目九:字符串相乘


题目一:把字符串转换成整数 (atoi)

题目描述:

函数 myAtoi(string s) 的算法如下:

  1. 读入字符串并丢弃无用的前导空格
  2. 检查下一个字符(假设还未到字符末尾)为正还是负号,读取该字符(如果有)。 确定最终结果是负数还是正数。 如果两者都不存在,则假定结果为正。
  3. 读入下一个字符,直到到达下一个非数字字符或到达输入的结尾。字符串的其余部分将被忽略。
  4. 将前面步骤读入的这些数字转换为整数(即,"123" -> 123, "0032" -> 32)。如果没有读入数字,则整数为 0 。必要时更改符号(从步骤 2 开始)。
  5. 如果整数数超过 32 位有符号整数范围 [−2^31,  2^31 − 1] ,需要截断这个整数,使其保持在这个范围内。具体来说,小于 −2^31 的整数应该被固定为 −2^31 ,大于 2^31-1 的整数应该被固定为 2^31−1 。
  6. 返回整数作为最终结果。

示例:

输入:s=“-42”

输出:-42

第1步:"42"(当前没有读入字符,因为没有前导空格)
         ^
第2步:"42"(当前没有读入字符,因为这里不存在 '-' 或者 '+')
         ^
第3步:"42"(读入 "42")
           ^
解析得到整数 42 。
由于 "42" 在范围 [-2^31, 2^31 - 1] 内,最终结果为 42 。

思路:首先我们可以根据题目的提示知道几点,对非法字符的判断,对加减号和空格的处理,以及对数字的最大界限问题!所以我们可以

  • 遍历字符串,对于空格直接跳过,对于”+“ ”-“ 我们可以新建一个sign去保存符号位,最后返回前判断正负即可
  • 碰到字符时,去判断是否为合法字符,非法直接返回,非法有两种情况,一个是非数字字符,二是转化的整数是否超过最大或者最低范围
  • 对于数字字符的处理,可以分为两步一是:字符转化为普通数字,就是减去’0‘的ASCII值即可,二是:数字的拼接
  • 最后在将结果乘以符号位sign即可

代码如下:

class Solution {
public:
    int myAtoi(string str) {
        //空字符串直接返回
        if(str.size()==0)
        {
            return 0;
        }
        
        int i=0;//下标
        int sign=1;//保存符号位,初始为1,默认为正数;
        int ret=0;//接收结果

        //跳过开头可能存在的空格
        while(str[i]==' '){
            if(++i==str.size())
                return 0;
        }

        //接着判断首个字符是否为正负号
        if(str[i]=='-'){
            sign=-1;  //符号位变负
        }
        if(str[i]=='-'||str[i]=='+'){
            ++i;
        }

        int Lrange=INT_MAX/10;//用来验证计算结果是否溢出int范围的数据

        //下面开始对非正负符号位进行判断
        for(int j=i;j<str.size();++j){

            if(str[j]<'0'||str[j]>'9')
                break;

//注意下面这句话要放在字符转换前,因为需要验证的位数比实际值的位数要少一位
//这里比较巧妙的地方在于
            // 1.用低于int型数据长度一位的数据Lrange判断了超过int型数据长度的值 
          // 2. 将超过最大值和低于最小值的情况都包括了
            if(ret>Lrange||(ret==Lrange&&str[j]>'7'))//判断是否越界
                return sign==1?INT_MAX:INT_MIN;

            ret=ret*10+(str[j]-'0');//拼接
        }
        return ret*sign;//返回乘以符号位
    }
};


题目二:字符串相加

描述:

  • 给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和并同样以字符串形式返回。
  • 你不能使用任何內建的用于处理大整数的库(比如 BigInteger), 也不能直接将输入的字符串转换为整数形式。

示例:

输入:num1="456",num2="77"

输出:"533"

思路:整体的思路就是同时遍历两个字符串,以两个都遍历结束为标志,每次取一个字符,然后将数字字符转化为相应的数字,再相加!相加后的结果可以使用insert函数进行头插到结果字符串中,但是前面已经说过insert效率太低,尽量少用!这题我们可以将结果进行尾插,使用+=运算符,然后在逆置整个字符串,效率上有提升!

细节点:①注意考虑来自低位的进位,相加是应该将其加上 ②数字字符转化为普通数字是实际上就是减去字符 '0',反过来就是加上字符'0'

代码如下:

class Solution {
public:
    string addStrings(string num1, string num2) {
        int CarBit=0;//初始化进位为0
        int end1=num1.size()-1,end2=num2.size()-1;
        string retstr;//接收结果的字符串

        //同时遍历
        while(end1>=0||end2>=0)
        {
            //可能其中短字符串已经遍历完,没有数字的位置用0补
            int val1=end1>=0?num1[end1--]-'0':0;
            int val2=end2>=0?num2[end2--]-'0':0;

            int ret=val1+val2+CarBit;//相加
            CarBit=ret/10;//取进位,加到下一位上
            ret%=10;//取个位
            retstr+=(ret+'0');//尾插到字符串,数字转化为数字字符
        }

        //加到最后可能还有进位没有加上去
        if(CarBit==1)
        {
            retstr+='1';
        }

        //翻转整个字符串
        reverse(retstr.begin(),retstr.end());
        return retstr;
    }
};

题目三:反转字符串

描述:

  • 编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。
  • 不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。

示例:

输入:s=["h","e","e","l","o"];

输出:["o","l","e","e","h"];

思路:十分的简单,但是要注意一点题目,原地修改,首先想到的方法那必然是双指针!个人感觉这题主要是去比较C和C++在书写代码上的差异,C++明显会简洁很多!

代码如下:

class Solution {
public:
    void reverseString(vector<char>& s) {
     int dest=0,current=s.size()-1;
     while(dest<current)
     {
         swap(s[dest++],s[current--]);
     }
    }
};

如果是C那么得完成swap的编写,代码量上会比C++的要多!对于这个函数C++标志库里面有,直接用就好了!注意这题只是代码量上的区别,思路C和C++差不多

题目四: 字符串中的第一个唯一字符

描述:

给定一个字符串 s ,找到 它的第一个不重复的字符,并返回它的索引 。如果不存在,则返回 -1 

示例1

输入:s="leetcode"

输出:0

示例2

输入:s="aabb"

输出:-1

思路1:先定义一个大小为26计数数组(字母有26个嘛),遍历一遍字符串,以每个字符减去字符‘a’(默认字符‘a’的为第一个元素)的ASCII值作为下标,统计每个下标出现的次数(其实就是每个字符出现的次数)。这样计数数组的初始化完成!再从头遍历一遍字符串,次数为1的就是第一个唯一字符,返回其下标即可!

代码如下:

class Solution {
public:
    int firstUniqChar(string s) {
        int count[26]={0};//计数数组

        //统计每个字符出现的次数
        for(auto ch:s)
        {
            count[ch-'a']++;
        }
        for(size_t i=0;i<s.size();i++)
        {
            if(count[s[i]-'a']==1)
            {
                return i;
            }
        }

        return -1;
    }
};

思路2:分别对字符串进行正序查询和反序查询,如果两次查询的字符下标相等,那就说明该字符只出现一次!返回下标即可!(这个方法十分的巧妙,使用的是string类的find和rfind的函数)

代码如下:

class Solution {
public:
  int firstUniqChar(string s) 
  {
    for(int i=0; i<s.size(); ++i)
    {
      int index = s.find(s[i]);//记录正向下标
      int reverse_index = s.rfind(s[i]);  //记录反向下标
      if(index == reverse_index)//两次一样就返回
        return i;
    }
    return -1;
  }
};

题目五:验证回文结构

描述:

再将一个字符串大写转小写、并移除所有非字母数字字符之后,短语正着读和反着读都一样。则可以认为该短语是一个回文串 。

字母和数字都属于字母数字字符。

给你一个字符串 s,如果它是 回文串 ,返回 true ;否则,返回 false 

示例1:

输入:s="A man, a plan, a canal: Panama"

输出:true;

示例2:

输入:s='' '';

输出:true;

思路:

* 1. 忽略大小写,只考虑字母

* 2. 通过首尾比较的方式,判断其是否为回文!

写法一:

class Solution {
public:
    bool isPalindrome(string s) {

        //空串也是回文结构
        if (s.empty())
        {
            return true;
        }
        string tmp;//用于存放符合题目要求的字符串

        size_t i = 0;
        while (i < s.size())
        {
            //字符数字字符直接尾插到tmp
            if ((s[i] >= 'a' && s[i] <= 'z') || (s[i] >= '0' && s[i] <= '9'))
            {
                tmp += s[i];
            }
            
            //大写先转化成小写再尾插到tmp
            if (s[i] >= 'A' && s[i] <= 'Z')
            {
                s[i] += 32;//也可以用库函数tolower,即s[i]=tolower(s[i]);   
                tmp += s[i];
            }
            ++i;
        }

        //比较首尾字符是否一致
        int begin = 0, end = tmp.size() - 1;
        while (begin < end)
        {
            if (tmp[begin++] != tmp[end--])
            {
                return false;
            }
        }
        return true;
    }
};

写法二:

class Solution {
public:
  bool isDigtalOrWord(char ch)
  {
    if( (ch>='0' && ch<='9')
    || (ch>='A' && ch<='Z')
    || (ch>='a' && ch<='z'))
      return true;

    return false;
  }
  bool isPalindrome(string s) 
  {
    if(s.empty())
      return true;
    for(int i=0; i<s.size(); ++i)
    {
      s[i] = tolower(s[i]); //忽略大小写      
    }

    int left = 0;    
    int right = s.size()-1;
    while(left < right)
    {
      //找到左边第一个未比较的字母
      while(left<right && !isDigtalOrWord(s[left]))
        left++;

      //找到右边第一个未比较的字母
      while(left<right && !isDigtalOrWord(s[right]))
        right--;

      //左右两边字母若不相等,则不是回文
      if(s[left] != s[right])
        return false;

      left++;
      right--;

    }
    return true;

  }

};

题目六:字符串最后一个单词的长度

描述:

计算字符串最后一个单词的长度,单词以空格隔开,字符串长度小于5000。(注:字符串末尾不以空格为结尾)

输入描述:

输入一行,代表要计算的字符串,非空,长度小于5000。

输出描述:

输出一个整数,表示输入字符串最后一个单词的长度。

示例:

输入:
hello nowcoder

输出:
8

说明:
最后一个单词为nowcoder,长度为8  

思路很简单:使用string类中的rfind找到最后一个空格的位置,然后再使用substr截取空格后面的字符串即可!

细节点:①题目要求输入一行,那么使用cin或者scanf这样的输入是不行的,因为他们读到空格就会停下,所以应该使用string中的getline

②需要注意substr它是左闭右开区间的,我们应该从空格的下一个位置开始读取!

代码如下:
 

#include <iostream>
#include<string>
using namespace std;

int main() 
{
   string s;
   getline(cin,s);
   int index=s.rfind(' ');
   string tmp=s.substr(index+1);//空格的下一个位置开始读
   cout<<tmp.size()<<endl;
}

题目七:反转字符串Ⅱ(区间部分反转)

描述:

一个字符串,每计数至2k个,就反转前k个字符。

  • 如果剩余的字符串少于k个,那就将剩余的字符串全部反转
  • 如果剩余的字符串不足2k个,大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。

示例1:

输入:s=“abcdefg”,k=2

输出:"bacdfeg"

示例2:

输入:s="abcd",k=2

输出:"bacd"

思路:整体思路就是循环遍历至2k个字符的位置,反转前k个,然后判断当前位置加上k个字符是否越界,如果越界说明剩余的字符少于k个,就全部反转;没越界,那就是不足或者大于2k个,再完成对应的反转操作就好了。(值得注意的是题目中,每计数至2k个就反转,那其实我们可以一次走2k步,而不用一个个加)

代码如下:

class Solution {
public:
    string reverseStr(string s, int k) {

        for(size_t i=0;i<s.size();i+=2*k)//一次走2k步
        {
            if(i+k<s.size())//没越界,反转前k个
            {
                reverse(s.begin()+i,s.begin()+i+k);
            }

            else//越界了,直接反转剩余全部字符
            {
            reverse(s.begin()+i,s.end());
            }
        }
        return s;
    }
};

题目八:反转字符串中的单词

描述:

给定一个字符串 s ,你需要反转字符串中每个单词的字符顺序,同时仍保留空格和单词的初始顺序。

示例1:

输入:s = "Let's take LeetCode contest"

输出:"s'teL ekat edoCteeL tsetnoc"

示例2:

输入:s="Mr Ding"

输出:"rM gniD"

思路:相对简单

* 1. 通过查找空格,分割单词

* 2. 针对分割的单词进行翻转

所以这题我们可以使用双指针(并非真正的指针,下标也可以是指针)

代码如下:

class Solution {
public:
    string reverseWords(string s) {
        size_t cur=0;//遍历找空格指针
        size_t dest=0;//记录单词起点指针
        while(cur<s.size())
        {
            dest=cur;
            while(cur<s.size()&&s[cur]!=' ')//找到空格的位置
            {
                cur++;
            }
            reverse(s.begin()+dest,s.begin()+cur);//反转单词
            cur++;//下一个单词的起点位置
        }
        return s;
    }
};

题目九:字符串相乘

描述:

给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。

注意:不能使用任何内置的 BigInteger 库或直接将输入转换为整数。

示例1:

输入: num1 = "2", num2 = "3"

输出: "6"

示例2:

输入: num1 = "123", num2 = "456"

输出: "56088"

思路:我们可以模拟手算的过程,这里num1是被乘数,num2是乘数

  • 如果num1和num2是之一为"0",直接返回"0"
  • 如果都不是,那就模拟手算过程,从右往左遍历乘数,用num2的每一位去乘以num1的每一位,将每次结果保存,最后结果相加(调用字符串相加的函数)
  • 但是值得注意的是每次的结果需要进行错位相加,这个我们可以通过补0来完成!乘数的个位与被乘数的每一位相乘后的结果不需补0,十位补1个0,百位补2个0……以此类推

还有注意乘法也是有进位的!!!

代码如下:

class Solution {
public:
    void AddString(string& retst, string tmp)//这里传引用,是因为我想改变外面的值
    {
        string tmp1(retst);//这里拷贝,是因为我要将接受结果的串先重置

        retst = "";//重置为空,不重置的话下面的结果就会在上一次的结果上继续尾插

        //正常字符串相加
        int CarBit = 0;//初始化进位为0
        int end1 = tmp1.size() - 1, end2 = tmp.size() - 1;
        while (end1 >= 0 || end2 >= 0)
        {
            int val1 = end1 >= 0 ? tmp1[end1--] - '0' : 0;
            int val2 = end2 >= 0 ? tmp[end2--] - '0' : 0;

            int ret = val1 + val2 + CarBit;//相加
            CarBit = ret / 10;//取进位,加到下一位上
            ret %= 10;//取个位
            retst += (ret + '0');//尾插到字符串,数字转化为数字字符
        }

        //加到最后可能还有进位没有加上去
        if (CarBit == 1)
        {
            retst += '1';
        }
        //翻转整个字符串
        reverse(retst.begin(), retst.end());
    }
    string multiply(string num1, string num2) {
        if (num1 == "0" || num2 == "0")
        {
            return "0";
        }
        string retstr;//接收最终结果
        string tmp;//用于存放每次的结果
        int i = 0;
        int j = 0;
        int flag = -1;//记录个、十、百位,对应补0
        for (i = num2.size() - 1; i >= 0; --i)
        {
            flag += 1;//,从个位开始,每次移一位
            int CarBit = 0;//存放每一位相乘的进位

            tmp = "";//每次都要置为空串,因为上一位相乘的结果还保留
            for (j = num1.size() - 1; j >= 0; --j)
            {
                int ret = 0;
                ret= (num2[i] - '0') * (num1[j] - '0') + CarBit;
                CarBit = ret / 10;
                ret %= 10;
                //tmp.insert(tmp.begin(), ret + '0');//头插,效率较低
                tmp+=(ret+'0');
            }
            //可能还会有进位
            if(CarBit)
            {
                //tmp.insert(tmp.begin(), CarBit + '0');
                tmp+=(CarBit+'0');
            }

            //补0
            int k = 0;
            for (; k < flag; k++)
            {
                tmp += '0';
            }

            //逆置0,这里-k是因为补了0,我们并不想逆置0,只想逆置0前面的数字
            //当然了,如果前面的数字用的是头插insert函数那就不需要逆置
            reverse(tmp.begin(),tmp.end()-k);
            
            //每次结果相加
            AddString(retstr, tmp);
        }
        return retstr;
    }
};

其实两段代码有很多相似的地方,但是相乘要注意的细节很多,我们可以通过调试去理解体会这个过程!!!对于这道题,还有一个思路,用数组存结果,大家可以看官方题解!


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值