下面的题目是对string中的常见函数的使用,便于熟悉string类!
目录
题目一:把字符串转换成整数 (atoi)
题目描述:
函数
myAtoi(string s)
的算法如下:
- 读入字符串并丢弃无用的前导空格
- 检查下一个字符(假设还未到字符末尾)为正还是负号,读取该字符(如果有)。 确定最终结果是负数还是正数。 如果两者都不存在,则假定结果为正。
- 读入下一个字符,直到到达下一个非数字字符或到达输入的结尾。字符串的其余部分将被忽略。
- 将前面步骤读入的这些数字转换为整数(即,"123" -> 123, "0032" -> 32)。如果没有读入数字,则整数为
0
。必要时更改符号(从步骤 2 开始)。- 如果整数数超过 32 位有符号整数范围
[−2^31, 2^31 − 1]
,需要截断这个整数,使其保持在这个范围内。具体来说,小于−2^31
的整数应该被固定为−2^31
,大于2^31-1
的整数应该被固定为2^31−1
。- 返回整数作为最终结果。
示例:
输入: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; } };
其实两段代码有很多相似的地方,但是相乘要注意的细节很多,我们可以通过调试去理解体会这个过程!!!对于这道题,还有一个思路,用数组存结果,大家可以看官方题解!