文章目录
string相关习题
仅仅反转字母
思路
双指针
定义两个指针一个指向字符串首元素,另一个指向字符串尾元素,两个指针分别从首尾开始走, 遇到非字母自负直接跳过, 遇到字母字符两者交换
示例代码
class Solution {
public:
bool isletter(char s)
{
return (s>='a'&&s<='z') || (s>='A'&&s<='Z');
}
string reverseOnlyLetters(string s) {
int begin=0, end=s.size()-1;
while(begin<end)
{
//非英文字母直接跳过
while(begin<end && !isletter(s[begin]))
++begin;
while(begin<end && !isletter(s[end]))
--end;
//英文字母交换
swap(s[begin],s[end]);
++begin;
--end;
}
return s;
}
};
验证一个字符串是否是回文串
思路
双指针
首先将所有大写字母转小写或小写字母转大写,解题思路与上一题相同, 遇到非字母数字字符直接跳过,是字母数字字符时,判断两个指针指向字符是否相等,不相等直接返回false, 相等继续走
示例代码
class Solution {
public:
bool isletternumber(char ch)
{
return (ch>='A' && ch<='Z') || (ch>='a' && ch<='z') || (ch>='0' && ch<='9');
}
bool isPalindrome(string s) {
//所有大写字母转小写
for(auto& ch: s)
{
if(ch>='A'&& ch<='Z')
ch+=32;
}
int begin=0, end=s.size()-1;
while(begin<end)
{
//非字母数字字符直接跳过
while(begin<end && !isletternumber(s[begin]))
++begin;
while(begin<end && !isletternumber(s[end]))
--end;
//是字母数字字符判断前者和后者是否相等
if(s[begin]!=s[end])
{
return false;
}
else
{
++begin;
--end;
}
}
return true;
}
};
总结
此类题目的思路:双指针,通过定义两个指针分别走根据题目要求来解题
练习题目: 345. 反转字符串中的元音字母 - 力扣(LeetCode)
替换空格
思路1
以空间换时间
- 定义一个新的字符串, 计算好原字符串空格个数, 提前开新字符串的空间, 避免+=时频繁扩容
- 从前往后遍历原字符串, 遇到空格新字符串+=“%20”, 否则+=原字符串的值
示例代码1
class Solution {
public:
string replaceSpace(string s) {
string newstr;
int num=0;
//计算空格的个数
for(auto ch:s)
{
if(ch==' ')
++num;
}
//提前开空间, 避免+=时频繁扩容
newstr.reserve(s.size()+num*2);
for(int i=0;i<s.size();++i)
{
if(s[i]==' ')
{
newstr+="%20";
}
else
{
newstr+=s[i];
}
}
return newstr;
}
};
思路2
find+replace
- 先计算空格个数,提前给原字符串扩容, 避免replace时频繁扩容
- 调用find函数找空格, 找到空格就用replace替换成"%20", 继续找继续替换
示例代码2
class Solution {
public:
string replaceSpace(string s) {
int num=0;
//计算空格的个数
for(auto ch:s)
{
if(ch==' ')
++num;
}
//提前开空间, 避免replace扩容
s.reserve(s.size()+num*2);
//find+replace
size_t pos=s.find(' ');
while(pos != string::npos)
{
s.replace(pos, 1,"%20");
pos=s.find(' ',pos+3);
}
return s;
}
};
小补充
find_first_of
将字符串中的"abc"都替换成 ‘*’, 直接使用find_first_of 查找替换即可
int main()
{
string str("Please, replace the vowels in this sentence by asterisks.");
size_t found = str.find_first_of("abc");
while (found != string::npos)
{
str[found] = '*';
found = str.find_first_of("abc", found + 1);
}
cout << str << endl;
return 0;
}
URL化
思路1
以空间换时间
- 定义一个新的字符串, 计算好原字符串空格个数, 提前开新字符串的空间, 避免+=时频繁扩容
- 从前往后遍历原字符串, 遇到空格新字符串+=“%20”, 否则+=原字符串的值
示例代码1
class Solution {
public:
string replaceSpaces(string S, int length) {
string newstr;
int num=0;
//计算空格的个数
for(int i=0;i<length;++i)
{
if(S[i]==' ')
++num;
}
//提前开空间, 避免+=时频繁扩容
newstr.reserve(length+num*2);
for(int i=0;i<length;++i)
{
if(S[i]==' ')
{
newstr+="%20";
}
else
{
newstr+=S[i];
}
}
return newstr;
}
};
思路2
双指针
一个指针end指向字符串的尾元素, 另一个指针i指向字符串真实长度的最后一个元素, 从后往前遍历字符串, 遇到空格, 给end指向的位置依次赋值’0’ ‘2’ ‘%’, 否则直接将i指向元素赋值给end指向位置直至结束, 挪动完数据后,删除多余字符, 用substr函数
示例代码2
class Solution {
public:
string replaceSpaces(string S, int length) {
//双指针,从后往前遍历字符串
int end=S.size()-1;
int i=length-1;
while(i>=0 &&end>=0)
{
if(S[i]==' ')
{
S[end]='0';
S[end-1]='2';
S[end-2]='%';
end-=3;
}
else
{
S[end]=S[i];
--end;
}
--i;
}
//挪动完数据后,删除多余字符
if(end>=0)
S=S.substr(end+1);
return S;
}
};
字符串最后一个单词的长度
思路
直接使用string提供的rfind函数, 从后往前找遇到空格相减返回字符串,如果该字符串中没有空格, 返回原本字符串的长度即可
注意: 不要使用cin>>line,因为会它遇到空格就结束了
示例代码
#include <iostream>
using namespace std;
int main() {
string str;
getline(cin,str);
size_t pos=str.rfind(' ');
if(pos!=string::npos)
cout<<str.size()-pos-1<<endl;
else
cout<<str.size()<<endl;
}
最后一个单词的长度
思路
乍一看以为是和上一题一样的方法都使用rfind函数,但此题多了一个条件:单词前后用一些空格字符隔开, 所以不直接用rfind函数。
思路:定义一个指针指向字符串最后一个元素, 如果字符串结尾有空格先跳过空格, 继续向前走,遇到非空格元素把定义的长度++, 直至遇到最后一个单词前的空格跳出循环
示例代码
class Solution {
public:
int lengthOfLastWord(string s) {
int end=s.size()-1;
int len=0;
//跳过字符串末尾空格
while(s[end]==' ')
--end;
while(end>=0 && s[end]!=' ')
{
++len;
--end;
}
return len;
}
};
补充
获取文件后缀
rfind+substr
从后往前找, 找到后用substr来截取
int main()
{
string file("string.cpp.tar.zip");
size_t pos = file.rfind('.'); //倒着找
if (pos != string::npos)
{
string stuffix = file.substr(pos, file.size() - pos);
cout << stuffix << endl;
}
return 0;
}
获取网址域名
find+substr
先找到 : // 的位置,记录其后一个位置, 再找/的位置记录下来,截取这两个中间的位置
int main()
{
string url("http://www.cplusplus.com/reference/string/string/find/");
cout << url << endl;
size_t start = url.find("://");
if (start == string::npos)
{
cout << "invalid url" << endl;
}
//取域名
start += 3;
size_t finsh = url.find('/',start);
string address = url.substr(start, finsh-start);
cout << address << endl;
}
字符串中第一个唯一字符
思路
计数排序的思想
分为2步:
- 对原字符串的每个字符进行映射,统计每个字符出现的次数
CountA[ch-'a']++
- 遍历求各个字符出现的次数即
CountA[s[i]-'a']
,只出现一次的字符
示例代码
#include <iostream>
using namespace std;
int main() {
string s;
cin>>s;
int CountA[26]={
0};
//统计每个字符出现的次数
for(auto ch:s)
{
CountA[ch-'a']++; //将小写字母映射到0~25之间
}
//遍历打印出出现一次的字符
for(int i=0;i<s.size();++i)
{
if(CountA[s[i]-'a']==1)
cout<<s[i]<<endl;
return 0;
}
}
cout << showpos << -1 << endl;
}
判定字符是否唯一
思路
计数排序的思想
思路和上一题完全相同,这里存在字符出现次数大于1返回false, 否则返回true
示例代码
class Solution {
public:
bool isUnique(string astr) {
int count[26]={
0};
for(auto ch:astr)
{
count[ch-'a']++;
}
for(int i=0;i<astr.size();++i)
{
if(count[astr[i]-'a']>1)
return false;
}
return true;
}
};
第一个只出现一次的字符
思路
计数排序的思想
思路和前两题完全相同, 唯一注意点是这里的映射关系, 字符串中大小写字母都可能出现, 所以重新计算一下数组的大小; ASCLL: ‘A’ : 65 ‘z’ : 122, 以A为映射最初的点, 122-65=57 , 即数组中最后一个元素下标是57, 所以数组大小是58
示例代码
class Solution {
public:
int FirstNotRepeatingChar(string str) {
int count[58]={
0}; // 'A': 65 'z': 122 122-65=57
for(auto ch:str)
count[ch-'A']++;
for(int i=0;i<str.size();++i)
{
if(count[str[i]-'A']==1)
return i;
}
return -1;
}
};
反转字符串 II
思路
从左向右遍历字符串,每次i+=2k ,如果从剩余字符小于 2k但大于或等于 k个,则反转前 k个字符 ,否则全部翻转
示例代码
class Solution {
public:
string reverseStr(string s, int k) {
for(int i=0;i<s.size();i+=2*k)
{
if(i+k<=s.size())
{
reverse(s.begin()+i,s.begin()+i+k);
}
else
{
reverse(s.begin()+i,s.end());
}
}
return s;
}
};
反转字符串中的单词 III
思路
从左向右遍历字符串,遇到空格反转这一个单词开头到空格位置的字符, 更新好每次反转位置,下一次开始反转的位置 = 空格位置 + 1,最后一个单词遍历过程中无法处理, 直至其他单词反转完成后再特殊处理最后一个单词。
示例代码
class Solution {
public:
string reverseWords(string s) {
int start=0;
for(int i=0;i<s.size();++i)
{
//反转区间左闭右开
if(s[i]==' ')
{
reverse(s.begin()+start, s.begin()+i);
//下一次开始反转的位置 = 空格位置 + 1
start=i+1;
}
}
//最后一个单词特殊处理
reverse(s.begin()+start, s.end());
return s;
}
};
把字符串转换成整数
思路
定义一个flag保留’+‘和’-',一个sum保留字符串转成数字后相加的结果,以下几种情况:
- 出现字母字符直接返回,用isalpha判断
- 处理’+‘和’-‘,’+‘时falg置成1,’-'时falg置成-1
- 是数字字符转成数字后直接运算
- 最后处理运算结果的正负
示例代码
class Solution {
public:
int StrToInt(string str) {
int flag=1, sum=0;
for(auto ch:str)
{
if(isalpha(ch)) //出现字母字符直接返回
return 0;
if(ch =='+' ||ch =='-' ) //处理'+'和'-'
flag = (ch == '+') ? 1:-1;
if(isdigit(ch)) //数字字符的运算
sum=sum*10+ch-'0';
}
return flag*sum;
}
};
字符串相加
思路
- 首先获取两个字符串的最后一位
- 定义一个新的字符串str, 提前开好空间(两数相加所得到的位数就是两数中最大位数的那个数的位数+1)
- 将获取两个字符串的这一位相加,相加后处理进位的问题,ret只保留一位结果,next中保存进位, 将ret的值转换成字符形式尾插到str中,循环继续加其他位
- 所有位数相加完毕后,将str字符串逆置过来
注意:
步骤3要防止取到位数end1和end2越界的问题,如果越界将这一位置成0
步骤4之前要特殊处理最后一次进位有剩余,没进上的那个’1’
示例代码
class Solution {
public:
string addStrings(string num1, string num2) {
//获取两个字符串的最后一位
int end1=num1.size()-1, end2=num2.size()-1;
int next=0; //进位
//两数相加所得到的位数就是两数中最大位数的那个数的位数+1
string str;
str.reserve(num1.size()>num2.size()?num1.size()+1:num2.size()+1);
//两个字符串对应各个位数加完了才算结束
while(end1>=0 || end2>= 0)
{
//防止end1和end2越界,即end1<0 或 end2<0
int val1 = end1 >=0 ?num1[end1]-'0':0;
int val2 = end2 >=0 ?num2[end2]-'0':0;
int ret=val1+val2+next;
//处理进位
next=ret/10;
ret%=10;
str +=('0'+ret); //直接尾插到str的后面
--end1;
--end2;
}
//处理最后一次进位有剩余,没进上
if(next == 1)
str+='1';
//字符串反转过来
reverse(str.begin(),str.end());
return str;
}
};