C++STL之string类:相关习题解析

目录

1,字符串转整型数字

2,字符串最后一个单词的长度(getline的使用)

3,仅仅反转字母

4,字符串中的第一个唯一字符(找字符串中第一个只出现一次的字符)

5,验证回文串

6,验证回文串||

7,字符串轮转

8,翻转字符串|||:翻转字符串中的单词

9,字符串中的单词反转

10,字符串相加

11,字符串相乘


1,字符串转整型数字

. - 力扣(LeetCode)

我们先来看这个:字符串 "12345" 转成整数 12345 怎么转呢?  

第一次循环时,val 初始为 0,乘以 10 还是 0,加上 '1' - '0' 即 1,val 变为 1。

第二次循环,val 为 1,乘以 10 得到 10,加上 '2' - '0' 即 2,val 变为 12,以此类推,最终得到 12345 并输出。

#include <iostream>
#include <string>
using namespace std;
int main()
{
	string s("12345");
	int val = 0;
	for (size_t i = 0; i < s.size(); ++i) 
	{
		val *= 10;
		val += s[i] - '0';
	}
	cout << val << endl;
	return 0;
}

有了该基础,做这个题目就很轻松了

请你来实现一个 myAtoi(string s) 函数,使其能将字符串转换成一个 32 位有符号整数(类似 C/C++ 中的 atoi 函数)。

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

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

由题意得

        ① 我们先去掉前导空格,就是把前面的空格都去掉。

        ② 判断该字符串最后的正负

        ③字符串转整数 和上的转的逻辑类似,只不过多了一步需要检查溢出,因为题目说是一个 32 位有符号整数。if (val > (INT_MAX / 10) || (val == (INT_MAX / 10) && digit > 7)) :因为 INT_MAX 是 2147483647,如果当前累加的结果 val 已经大于 INT_MAX / 10 ,或者 val 等于 INT_MAX / 10 并且即将累加的数字 digit 大于 7(因为 2147483647 的十位是 4,下一位最大只能是 7),那么就会发生溢出,此时根据正负号返回 INT_MAXINT_MIN

class Solution {
public:
    int myAtoi(string str) 
    {
        //去掉前导空格
        int i = 0;
        while(i < str.size() && str[i] == ' ') 
        {
            ++i;
        }
        //判断正负
        int flag = 1;
        if(i < str.size() && (str[i] == '-'|| str[i] == '+') ) 
        {
          flag = (str[i] == '-') ? -1 : 1;
          ++i;
        } 
        //字符数转整数
        int val = 0;
        int digit = 0;
        for(; i < str.size() && isdigit(str[i]); ++i) 
        {
           
            digit = str[i] - '0';
            //检查是否溢出
             if (val > (INT_MAX / 10) ||
              (val == (INT_MAX / 10) && digit > 7)) 
                {
	                return (flag == 1)? INT_MAX : INT_MIN;
	            }
            val *= 10;
            val += digit;
        }
        return  val * flag;
    }
};

2,字符串最后一个单词的长度(getline的使用)

牛客:

字符串最后一个单词的长度_牛客题霸_牛客网

std::getline(std::cin, str) 会从标准输入(通常是键盘)读取一行文本,并将其存储到

str 字符串中。而 C++ 中的 cin 去读取的话,遇到空格或回车就会结束

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

int main() 
{
    string s;
    getline(cin,s);
    size_t pos = s.rfind(' ');
    cout << s.size() - pos -1 << endl;    
}

力扣:

. - 力扣(LeetCode)

  • int end = s.size() - 1; :初始化一个索引 end ,使其指向字符串 s 的最后一个字符。

  • 接下来的 while 循环从字符串的末尾开始向前移动,跳过所有的空格字符。如果当前字符是空格,就将 end 减 1;如果遇到非空格字符,就跳出循环。

  • 如果经过上述循环后 end 变成了 -1,说明整个字符串都是空格,返回 0 。

  • int pos = s.rfind(' ', end); :使用 rfind 函数在字符串中从位置 end 往前查找第一个空格的位置,并将其索引存储在 pos 中。

  • 最后,通过 end - pos 计算出最后一个单词的长度并返回。

class Solution {
public:
    int lengthOfLastWord(string s) 
    {
        int end = s.size() - 1;
        while(end >= 0) 
        {
            if(s[end] == ' ') 
                --end;
            else
                break;
        }
        if(end == -1 )
            return 0;
         int pos = s.rfind(' ', end);
         return end - pos;
    }
};

3,仅仅反转字母

. - 力扣(LeetCode)

① 跳过非英文字母。并定义了一个辅助函数 isenglish, 用于判断一个字符是否为英文字母(包括小写和大写)。

② 遇到英文字母就进行交换。

class Solution {
public:
    bool isenglish(char c) 
    {
        if((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) 
        {
            return true;
        }
        return false;
    }
    string reverseOnlyLetters(string s) 
    {
        //
        int start = 0;
        int end = s.size() - 1;
        while(start < end)
        {
            //跳过非英文字母
            while(start < end && !isenglish(s[start]))
            {
                ++start;
            }
            while(start < end && !isenglish(s[end]))
            {
                --end;
            }
            swap(s[start],s[end]);
            ++start;
            --end;
        }
        return s;
    }
};

4,字符串中的第一个唯一字符(找字符串中第一个只出现一次的字符)

. - 力扣(LeetCode)

此题解法和计数排序的思想有点类似。

创建一个整数数组 count ,大小为 26,用于统计每个小写字母出现的次数。通过遍历字符串 s ,将每个字符 ch 对应的计数加 1, count[ch - 'a']++ 利用字符与 'a' 的差值作为索引来对应相应的字母)。

 

然后,再次遍历字符串 s ,对于每个字符,如果其在 count 数组中的计数为 1,就返回其索引 i

 

如果遍历完整个字符串都没有找到只出现一次的字符,就返回 -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;
    }
};

5,验证回文串

. - 力扣(LeetCode)

① 如果是空串就是回文。

② 跳过非字母字符以及数字

③ 都转成小写进行比较

class Solution {
public:
    bool iselglishchar(char ch) 
    {
        if((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') 
        || (ch >= '0' && ch <= '9')) 
        {
            return true;
        }
        return false;
    }
    bool isPalindrome(string s) 
    {
        if(s == " " ) 
        {
            return true;
        }
        int start = 0;
        int end = s.size() - 1;
        while(start < end) 
        {
            //跳过非字母字符
            while(start < end && !iselglishchar(s[start]))
            {
                ++start;
            } 
            while(start < end && !iselglishchar(s[end])) 
            {
                --end;
            }
            if(tolower(s[start]) !=  tolower(s[end])) 
            {
                return false;
            }
            ++start;
            --end;
        }
        return true;
    }
};

6,验证回文串||

. - 力扣(LeetCode)

以 "abdda" 这个串为例,此时 i 指向'b',j 指向'd',发现不对了。

但是有一次删除的机会,我们自己写几个情况其实就能发现,此时子串范围为(i+1, j)

或 (i, j-1) 的两个子串只要有任意一个是回文串,则结果就是回文串,否则就不是。

当 "ab" 我们随便删除一个都是 true,所以"ab" 删除之后,返回的是 true。

class Solution {
public:
    bool isVild(string s, int start, int end) 
    {
        while(start < end) 
        {
            if(s[start] != s[end])
            {
                return false;
            }
            ++start;
            --end;
        }
        return true;
    }
    bool validPalindrome(string s) 
    {
        int start = 0;
        int end = s.size() - 1;
        while(start < end) 
        {
            if(s[start] != s[end]) 
            {
                return  isVild(s,start+1, end) || isVild(s,start,end-1);
                    // (s,1,1)       // (s.0,0)
            }
            ++start;
            --end;
        }
        return true;
    }
};

7,字符串轮转

. - 力扣(LeetCode)

① s1.size() == s2.size() :首先检查两个字符串的长度是否相等。如果长度不相等,那么它们肯定不是翻转关系,直接返回 false

② (s2 + s2).find(s1)!= string::npos :如果长度相等,那么将 s2 自身连接起来得到 s2 + s2 。然后在这个新字符串中查找 s1 。如果能找到(即 find 函数的返回值不为 string::npos ,表示未找到的特殊值),说明 s1 可以通过某种方式在 s2 的翻转中得到,返回 true ;如果找不到,返回 false

class Solution {
public:
    bool isFlipedString(string s1, string s2) 
    {
        return s1.size() == s2.size() && (s2 + s2).find(s1) != string::npos;
    }
};

8,翻转字符串|||:翻转字符串中的单词

. - 力扣(LeetCode)

① 函数 _reverseWords 用于反转指定范围内的字符顺序。通过一个循环,不断交换起始位置 start 和结束位置 end 的字符,实现反转。

 

② 函数 reverseWords 首先定义一个起始位置 start ,然后遍历输入字符串 s 。当遇到空格时,调用 _reverseWords 函数反转从 start 到当前空格前一位(即 i - 1 )的单词。然后更新 start 为空格后的位置(即 i + 1 )。

 

③ 遍历结束后,再单独处理最后一个单词,因为它后面没有空格来触发反转。

 

④ 最终返回反转单词顺序后的字符串 s

 

比如说,对于输入字符串 "hello world" ,首先会反转 "hello""olleh" ,然后更新 start ,接着反转 "world""dlrow" ,得到最终结果 "olleh dlrow"

class Solution {
public:
    // 反转单词
    void _reverseWords(string& s, int start, int end) 
    {
        while (start < end) 
        {
            char tmp = s[start];
            s[start++] = s[end];
            s[end--] = tmp;
        }
    }

    string reverseWords(string s) 
    {
        int start = 0;
        for (size_t i = 0; i < s.size(); ++i) 
        {
            if (s[i] ==' ') 
            {
                _reverseWords(s, start, i - 1);
                start = i + 1;
            }
        }
        // 处理最后一个单词
        _reverseWords(s, start, s.size() - 1);
        return s;
    }
};

9,字符串中的单词反转

移除元素:

. - 力扣(LeetCode)

① 定义一个慢指针和一个快指针

② fast 指向我们想要获取的元素。

③ slow 指向获取快指针获取的元素所指向新的位置 ;

④ 当快指针指向了需移除的元素时,快指针跳过他,因为这个元素不是我们需要的,到达下一个位置,下一个位置不是要移除的元素就是,我们需要的元素,所需元素就复制给 slow 指针。

class Solution {
public:
    int removeElement(vector<int>& nums, int val) 
    {
        int slow = 0;
        for(int fast = 0; fast < nums.size(); ++fast) 
        {
            if(nums[fast] != val) 
            {
                nums[slow] = nums[fast];
                ++slow;
            }
        }
        return slow;
    }
};

单词反转:

. - 力扣(LeetCode)

该题目的关键就是处理空格。也是我们第一步需要做的事情。

这个题目和轮转数组类似,只不过多了一步,需要处理多余的空格。

① 处理空格

        处理空格的思路就和上面的移除元素类似的,我们假设单词间隔只是一个空格,就更好理解了,如下图所示:

       我们处理完空格之后,也就意味着长度要变小,所以用 resize 来调整字符串的长度。

② 每个单词反转

③ 整体单词反转

class Solution {
public:
    string reverseMessage(string message) 
    {
        //1,处理空格
        int slow = 0;
        for(int fast = 0; fast < message.size(); ++fast) 
        {
            if(message[fast] != ' ') 
            {
                if(slow != 0) 
                {
                    message[slow++] = ' '; //就算存在很多空格也会被覆盖
                }
                while(fast < message.size() && message[fast] != ' ')
                {
                    message[slow++] = message[fast++];
                }
            }
        }
        message.resize(slow);
        //2,单词反转
        int pos = 0;
        for(int i = 0; i < message.size(); ++i) 
        {
            if(message[i] == ' ') 
            {
                if(pos < i)
                    reverse(message.begin() + pos, message.begin() + i);
                pos = i + 1;
            }
        }
        //最后一个单词反转
        if(pos < message.size()) 
        {
             reverse(message.begin() + pos, message.end());
        }
        //整体翻转
        reverse(message.begin(), message.end());
        return  message;
    }
};

10,字符串相加

. - 力扣(LeetCode)

  • 初始化:

    • 定义两个指针 ij 分别指向两个字符串 num1num2 的末尾。
    • 初始化进位 cur 为 0 。
    • 创建一个空字符串 result 用于存储相加的结果。
  • 进入循环,只要以下三个条件之一满足就继续循环:

    • i 大于等于 0,即 num1 还有未处理的数字。
    • j 大于等于 0,即 num2 还有未处理的数字。
    • 进位 cur 大于 0,表示进位了(因为最后加完) 。
  • 在循环内部:

    • 如果 i 大于等于 0 ,获取 num1 对应位置的数字并减去字符 '0' 的值,得到实际数字 sum1 ,如果 i 小于 0 ,则 sum1 为 0 。
    • 同样,如果 j 大于等于 0 ,获取 num2 对应位置的数字并减去字符 '0' 的值,得到实际数字 sum2 ,如果 j 小于 0 ,则 sum2 为 0 。
  • 计算当前位的总和 sumsum1 + sum2 + cur

  • 将总和对 10 取余,加上字符 '0' 的值,添加到结果字符串 result 中。

  • 更新进位 cur 为总和除以 10 的值。

  • 如果 i 大于等于 0 ,将 i 减 1 ;如果 j 大于等于 0 ,将 j 减 1 。

  • 循环结束后,反转结果字符串 result

  • 返回最终的结果字符串。

class Solution {
public:
    string addStrings(string num1, string num2) 
    {
        //倒着加,记录进位,保存数组到字符串,翻转字符串
        int i = num1.size() - 1;
        int j = num2.size() - 1;
        int cur = 0;
        string result = "";
        while(i >= 0 || j >= 0 || cur > 0) 
        {
            int sum1 = (i >= 0) ? (num1[i] - '0') : 0;
            int sum2 = (j >= 0) ? (num2[j] - '0') : 0;
            int sum = sum1 + sum2 + cur;
            result += sum % 10 + '0';  //两个数相加的结果
            cur = sum / 10;  //看看是否进位 1,没有就是 0
            if(i >= 0)
                --i;
            if(j >= 0) 
                --j;
        }
        reverse(result.begin(),result.end()); //反转
        return result;
    }
};

11,字符串相乘

. - 力扣(LeetCode)

  • 首先,计算两个输入字符串 num1num2 的长度,并进行特殊情况判断。如果其中一个字符串表示的数字为 0(即长度为 1 且字符为 '0'),则直接返回 "0" 。

  • num1 分配一个整数数组 nums1 ,并将 num1 中的字符转换为对应的整数。

  • 初始化一个整数数组 sums 用于存储乘法运算的中间结果和最终求和结果,初始值都为 0 。

  • 进行乘法运算和累加:

    • 对于 num2 中的每个数字(从右往左),取出当前数字 multiply1
    • 对于 num1 中的每个数字(从右往左),计算 multiply1 与当前数字的乘积,并累加到 sums 数组中对应的位置(位置由两个数字的位置决定)。
  • sums 数组进行求和处理:

    • 如果某个位置的数字大于等于 10,将其除以 10 的商累加到下一位,当前位取余数。
  • sums 数组中的数字转换为字符并连接成字符串 sb 。如果最后一位是 0 且是唯一的非零数字,则跳过不添加。

  • 反转字符串 sb

  • 释放为 nums1sums 分配的内存。

  • 返回最终的结果字符串 sb

 

class Solution {
public:
    string multiply(string num1, string num2) 
    {
        //1,计算字符串的长度
        int len1 = num1.size();
        int len2 = num2.size();
        //其中一个为 0
        if(len1 == 1 && num1[0] - '0' == 0 || len2 == 1 && num2[0] - '0' ==0) 
        {
            return "0";
        }
        //第一个数转整数
        int* nums1 = new int[len1];
        for(int i = 0; i < len1; ++i) 
        {
            num1[i] = (num1[i] - '0');
        }
        //累加
        int* sums = new int[len1 + len2]();
        for(int i = 0; i < len2; ++i) 
        {
            int multiply1 = num2[len2 - 1 -i] - '0';
            for(int j = 0; j < len1; ++j) 
            {
                sums[i + j] += multiply1 * num1[len1 - 1 - j]; //第一波相对于一行,三行相加
            }
        }
        //求和 
        string sb = "";
        for(int i = 0; i < len1 + len2; ++i) 
        {
            if(sums[i] >= 10) 
            {
                sums[i + 1] += sums[i] / 10; //后一位加上前面的进位 +=
                sums[i] = sums[i] % 10;  // 第一位直接取余赋值
            }
            if(i == len1 + len2 -1 && sums[i] == 0)   //跳过最后,如果值为 0 防止把 '0' 赋值到字符串前面
                continue;
            sb += sums[i] + '0';
        }
        reverse(sb.begin(),sb.end());
        delete[] nums1;
        delete[] sums;
        return sb;
    }
};

  • 15
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值