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 = -3 ; 3%-4 = 3;
5%3 = 2 ; 5%-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 盛水最多的容器
- 盛最多水的容器
给你 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 去除重复字母
- 去除重复字母
给你一个字符串 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;
}