【字符转换相关】近期刷题训练--2021年秋招

0 引言

平时刷题过程中用,关于字符转换的问题 比比皆是。在此进行全面的总结,以便 快速解决和使用此方式进行题解。

0.1 string和int相互转换

0.1.1 string类型转换成int类型

方法一:使用atoi()函数

#include<bits/stdc++.h>
using namespace std;
int main(){
	string str="123";
	cout<<str<<"    str的类型为"<<typeid(str).name()<<endl;//输出Ss表示string类型
	//atoi的参数类型为(const char *nptr)而string是一个类,所以需要获取str的首地址即str.c_str()
	int num=atoi(str.c_str()); 
	cout<<num<<"    num的类型为"<<typeid(num).name()<<endl;//输出i表示int类型 
}

用devc++的typeid(num).name()输出是i,vs2017输出的是int,不同编译器这个函数输出可能不同啦。
还有一些类似的函数:

  • long atol(const char *nptr);把字符串转换成长整型数
  • double atof(const char *nptr);把字符串转换成浮点数
  • long int strtol(const char *str, char **endptr, int base);把参数 str 所指向的字符串根据给定的 base 转换为一个长整数

方法二:通过stringstream类转化

#include<bits/stdc++.h>
using namespace std;
int main() {
	stringstream sstream;
	string str="123";
	int num;
	cout<<"str="<<str<<"    str的类型为"<<typeid(str).name()<<endl;//输出Ss表示string类型
	sstream<<str;// 将string类型的值放入字符串流中
	sstream>>num;//将sstream中的第一条数据输出到num中 
	cout<<"num="<<num<<"    num的类型为"<<typeid(num).name()<<endl;//输出i表示int类型
}

0.1.2 int类型转换成string类型

方法一:使用to_string()函数

#include<bits/stdc++.h>
using namespace std;
int main() {
	int num=123;
	string str=to_string(num);
	cout<<"num="<<num<<"    num的类型为"<<typeid(num).name()<<endl;//输出i表示int类型
	cout<<"str="<<str<<"    str的类型为"<<typeid(str).name()<<endl;//输出Ss表示string类型
}

to_string(x)函数有多个重载,只要x是内置数值类型就可以。

方法二:通过stringstream类转化

#include<bits/stdc++.h>
using namespace std;
int main() {
	stringstream sstream;
	string str;
	int num=123;
	cout<<"num="<<num<<"    num的类型为"<<typeid(num).name()<<endl;//输出i表示int类型
	sstream<<num;// 将num类型的值放入字符串流中
	sstream>>str;//将sstream中的第一条数据输出到str中 
	cout<<"str="<<str<<"    str的类型为"<<typeid(str).name()<<endl;//输出Ss表示string类型
}

方法三:使用itoa()函数
函数原型:char * itoa(int value ,char *string ,int radix);
第一个参数是要转换的数字
第二个参数是要写入转换结果的目标字符串(字符型数组)
第三个参数是转移数字时所用的基数(进制)⭐(可以用来做进制转换)
返回值:指向string这个字符串的指针

#include<bits/stdc++.h>
using namespace std;
int main() {
	int num=123;
	char strc[100];
	string str=itoa(num,strc,10);//返回的是指向strc的指针,直接存进string类型即可
	cout<<"num="<<num<<"    num的类型为"<<typeid(num).name()<<endl;//输出i表示int类型
	cout<<"str="<<str<<"    str的类型为"<<typeid(str).name()<<endl;//输出Ss表示string类型
}

其他类似的函数:

  • litoa() 将长整型值转换为字符串
  • ultoa() 将无符号 长整型值转换为字符串

0.2 split实现(stringstream+getline实现)

vector<string> split(string str, char del) {
    stringstream ss(str);
    string temp;
    vector<string> ret;
    while (getline(ss, temp, del)) {
        ret.push_back(temp);
    }
    return ret;
}

0.3 注意string char* char[]三者转换

string转换为char *:
如果要将string直接转换成const char *类型。string有2个函数可以运用。

一个是.c_str(),一个是.data成员函数。

例子如下:

    string s1 = "abcde";
    const char *k = s1.c_str();
    const char *t = s1.data();
    printf("k:[%s] t:[%s]\n", k, t);
    system("pause");

0.4 删除string中指定字符

使用string::iterator(字符串迭代器)从开始 str.begin() 迭代到最后 str.end() ,再使用string.erase(const_iterator p)函数来删除迭代器所指向的字符。

#include <iostream>
#include <string>

using namespace std;

int main()
{
    string str;
    char ch;
    cin >> str;
    cin >> ch;
    string::iterator it;
    for (it = str.begin(); it < str.end(); it++)
    {
        if (*it == ch)
        {
            str.erase(it);
            it--;
            /*
            it--很重要,因为使用erase()删除it指向的字符后,后面的字符就移了过来,
            it指向的位置就被后一个字符填充了,而for语句最后的it++,又使it向后移
            了一个位置,所以就忽略掉了填充过来的这个字符。在这加上it--后就和for
            语句的it++抵消了,使迭代器能够访问所有的字符。
            */
        }
    }
    cout << str;
    return 0;
}

0.5 char & int 互相转换

ASCII 数字 转换为 char 见本节 1.3
注意:此处可以直接使用char进行ascii码转换为 char!—— (char)( (ch - 'A' + up_char_offset + 26) % 26 + 65)

1.ASCII法
推荐度:5星★★★★★

这是通用性最强的方法,也比较简单。缺点是只能一个一个转换

代码:

char cNum='5',result1;
int iNum=5,result2;
//char to num
result2=cNum-48;
//num to char
result1=iNum+48;

总之,int到char就是+48

char到int就是-48

2.函数法
推荐度:3星★★★

使用itoa、atoi(都在stdlib.h头文件)函数转换

操作简单,能一次性转换(不是一位一位的),不过有个很大的问题:itoa函数并非标准C的实现,只能在windows下编译通过,换句话说,如果你是写软件,没问题。但如果你是搞信息奥赛,那么这个函数不一定能通过编译。

具体的使用方法,请百度。

3.sprint法
推荐度:2星★★

此方法只能做到int转char。不过配合atoi,也能达到目的。是标准的C函数

代码:

int num=1234567;
sprintf(str, "%d", num);
//此时str就是“1234567”了。注意,str是个char数组

4.枚举判断法
推荐度:0星

额……就是if(a=’1′)b=1这样的……

都学了OI了,不至于还这么搞吧?

0.6 其它(负数除法和取余问题)

1:关于除法,不管是正数还是负数都是向0取整的:10/4 = 2,10/(-4) = -2

2:负数取余,通过取模来判定

|小| % |大| = |小| 符号同前 |大| % |小| = |余| 符号同前

3%4 = 3-3%4 = -3-3%-4 = -33%-4 = 35%3 = 25%-3 = 2-5%-3 = -2-5%3 = -2

3:浮点数转化为int整形时,小数部分会被省略,注意不是四舍五入~~

#include <iostream>
using namespace std;
 
int main(int argc,char** argv)
{
	int a = 8.5;
	cout<<a<<endl;//输出8
	cin.get();
	return 0;
}

4:严谨的情况下,不同的编译器在负数的除法运算下是存在不同情况的;但是在GCC编译器下,余数和被除数的符号一致

(防止有小白~“14➗2”读作“十四除以二”,除号前面的是“被除数”,除号后面的是“除数”)

记住这一点即可解决C/C++的大部分情况。取余运算时首先全取绝对值进行计算,再进行符号的判断

1.相关题目

1.1 盛水最多的容器

  1. 盛最多水的容器
    给你 n 个非负整数 a1,a2,…,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0) 。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
    说明:你不能倾斜容器。

示例 1:

输入:[1,8,6,2,5,4,8,3,7]
输出:49
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
示例 2:

输入:height = [1,1]
输出:1
示例 3:

输入:height = [4,3,2,1,4]
输出:16
示例 4:

输入:height = [1,2,1]
输出:2

我的题解

int maxArea(vector<int>& height) {
        int res = 0;
        int l = 0, r = height.size()-1;
        while(l < r){
            int nowHeight = height[l] < height[r] ? height[l++] : height[r--];   // 注意此处 后++/ --
            res = max(res, (r - l + 1)*nowHeight); 
        }
        return res;
    }

证明为什么正确——使用左右指针 依次缩小范围。
证明

为什么双指针的做法是正确的?

双指针代表了什么?

双指针代表的是 可以作为容器边界的所有位置的范围。在一开始,双指针指向数组的左右边界,表示 数组中所有的位置都可以作为容器的边界,因为我们还没有进行过任何尝试。在这之后,我们每次将 对应的数字较小的那个指针 往 另一个指针 的方向移动一个位置,就表示我们认为 这个指针不可能再作为容器的边界了。

为什么对应的数字较小的那个指针不可能再作为容器的边界了?

在上面的分析部分,我们对这个问题有了一点初步的想法。这里我们定量地进行证明。

考虑第一步,假设当前左指针和右指针指向的数分别为 x 和 y,不失一般性,我们假设 x≤y。同时,两个指针之间的距离为 t。那么,它们组成的容器的容量为:

min(x,y)∗t=x∗t

我们可以断定,如果我们保持左指针的位置不变,那么无论右指针在哪里,这个容器的容量都不会超过 x∗t了。 注意这里右指针只能向左移动,因为 我们考虑的是第一步,也就是 指针还指向数组的左右边界的时候。

我们任意向左移动右指针,指向的数为 y1,两个指针之间的距离为 t1,那么显然有 t1<t, 并且
在这里插入图片描述

因此有:

min(x,yt)∗t1<min(x,y)∗t
min(x,y )∗t <min(x,y)∗t

即无论我们怎么移动右指针,得到的容器的容量都小于移动前容器的容量。也就是说,这个左指针对应的数不会作为容器的边界了,那么我们就可以丢弃这个位置,将左指针向右移动一个位置,此时新的左指针于原先的右指针之间的左右位置,才可能会作为容器的边界。

这样以来,我们将问题的规模减小了 111,被我们丢弃的那个位置就相当于消失了。此时的左右指针,就指向了一个新的、规模减少了的问题的数组的左右边界,因此,我们可以继续像之前 考虑第一步 那样考虑这个问题:

  • 求出当前双指针对应的容器的容量;

  • 对应数字较小的那个指针以后不可能作为容器的边界了,将其丢弃,并移动对应的指针。

最后的答案是什么?

答案就是我们每次以双指针为左右边界(也就是「数组」的左右边界)计算出的容量中的最大值。

1.2 去除重复字母

  1. 去除重复字母
    给你一个字符串 s ,请你去除字符串中重复的字母,使得每个字母只出现一次。需保证 返回结果的字典序最小(要求不能打乱其他字符的相对位置)。
    注意:该题与 1081 https://leetcode-cn.com/problems/smallest-subsequence-of-distinct-characters 相同

示例 1:

输入:s = “bcabc”
输出:“abc”
示例 2:

输入:s = “cbacdcbc”
输出:“acdb”

我的题解


#include <algorithm>
#include <cmath>
#include <deque>
#include <iostream>
#include <list>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <unordered_map>
#include <map>
#include <vector>

using namespace std;



#define For(x,y,z) for(ll x = y; x < z;++x)
typedef long long ll;

class Solution {
public:

    /**
     * @Description: 使用栈  每次容器未访问过(因为字母要唯一);  容器不为空且栈顶元素大于当前元素 要出栈。
     * @param {string} s
     * @return {*}
     * @notes: 
     */
    string removeDuplicateLetters(string s) {
        unordered_map<char, int> strMap;
        for(char ch : s){
            strMap[ch] ++;
            // cout << "strmap[" << ch << "]:" << strMap[ch] << endl;
        }

        vector<int> visited(26, 0);
        stack<char> stackStr;
        for(char ch : s){
            if(!visited[ch - 'a']){
                // 一直进行判断  去除大的,而且后面有的时候
                while(!stackStr.empty() && stackStr.top() > ch){
                    // cout << stackStr.top() << endl;
                    if(strMap[stackStr.top()] > 0){ //  后面还有的话  // 不可以使用count 是计算有多少个key,一直有;而是使用里面的 num有几个!
                        visited[stackStr.top() - 'a'] = 0;
                        // cout << "strmap[" << ch << "]:" << strMap[ch] << endl;
                        // cout << "pop while:" << stackStr.top() << endl;
                        stackStr.pop();
                    }else{
                        break;
                    }
                }

                // stack为空 或者 当前元素唯一或不大于当前栈顶元素
                // 都是直接入栈
                visited[ch - 'a'] = 1;
                stackStr.push(ch);
                // cout << "not visited push ch:" << ch << endl;
            }
            // 访问过 --
            strMap[ch] --;
            
        }
        string res;
        while(!stackStr.empty()){
            res.push_back(stackStr.top());
            stackStr.pop();
        }
        reverse(res.begin(), res.end());
        return res;
    }
};


int main(int argc, char const *argv[])
{
    Solution s;
    cout << s.removeDuplicateLetters("cbacdcbc") << endl;
    system("pause");
    return 0;
}

1.3 二十六个字母前后循环开锁

我的题解


string dec(string str, int up_char_offset, int low_char_offset){
    string res;
    for( char ch : str ){
        // 大写
        if(ch>='A' && ch <= 'Z'){
            res.push_back( (char)(  (ch - 'A' + up_char_offset + 26) % 26 + 65)  );
        }else if (ch >= 'a' && ch <= 'z')
        {
            res.push_back( (char)( (ch - 'a' + low_char_offset + 26) % 26 + 97) );
        }else{
            res.push_back(ch);
        }
        
    }
    cout << res << endl;
    return res;

}


int main(int argc, char const *argv[])
{
    cout << dec("Def Z", -1, 1) << endl;
    system("pause");
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值