string相关习题

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)

344. 反转字符串 - 力扣(LeetCode)

替换空格

链接

在这里插入图片描述

思路1

以空间换时间

  1. 定义一个新的字符串, 计算好原字符串空格个数, 提前开新字符串的空间, 避免+=时频繁扩容
  2. 从前往后遍历原字符串, 遇到空格新字符串+=“%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

  1. 先计算空格个数,提前给原字符串扩容, 避免replace时频繁扩容
  2. 调用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

以空间换时间

  1. 定义一个新的字符串, 计算好原字符串空格个数, 提前开新字符串的空间, 避免+=时频繁扩容
  2. 从前往后遍历原字符串, 遇到空格新字符串+=“%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步:

  1. 对原字符串的每个字符进行映射,统计每个字符出现的次数 CountA[ch-'a']++
  2. 遍历求各个字符出现的次数即 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保留字符串转成数字后相加的结果,以下几种情况:

  1. 出现字母字符直接返回,用isalpha判断
  2. 处理’+‘和’-‘,’+‘时falg置成1,’-'时falg置成-1
  3. 是数字字符转成数字后直接运算
  4. 最后处理运算结果的正负

示例代码

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;
    }
};

字符串相加

链接

在这里插入图片描述

思路

  1. 首先获取两个字符串的最后一位
  2. 定义一个新的字符串str, 提前开好空间(两数相加所得到的位数就是两数中最大位数的那个数的位数+1)
  3. 将获取两个字符串的这一位相加,相加后处理进位的问题,ret只保留一位结果,next中保存进位, 将ret的值转换成字符形式尾插到str中,循环继续加其他位
  4. 所有位数相加完毕后,将str字符串逆置过来

注意:

  1. 步骤3要防止取到位数end1和end2越界的问题,如果越界将这一位置成0

  2. 步骤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;
    }
};

猜你喜欢

转载自blog.csdn.net/Ryujianli/article/details/129573507