1、数据分类处理
数据分类处理题目描述
收获:I 整数对应的数字需要连续包含R对应的数字(231包含23)
如何判断两个数连续包含:将两个数通过to_string®, to_string(I)函数转换为字符串,然后用find查找是否为子序列。
划重点!!!牛客上提交和牛客上用例运行与本地IDE不一致的问题:1. 检查题目是否要求有多组测试用例,所以输入必须是:
while(cin>>len){...}
而且每一次输入的初始化必须放到while循环里面,如果在while外面的话也会出错。
2、检查每一次的输出是否有:
cout<<endl;
不然多组输出就会连在一起输出!(暴风哭泣,有关于输入输出踩了好多坑)
#include<iostream>
#include<vector>
#include<algorithm>
#include<string>
using namespace std;
//判断I是否连续包含R对应的数字
bool match(int m, int n)
{
string str1 = to_string(m);
string str2 = to_string(n);
int pos = str2.find(str1);
if (pos != -1)
return true;
else
return false;
}
//bool IsSub(int R, int I) {
// int len = 0;
// if (R == 0) len = 0;
// while (R > 0) {
// R /= 10;
// len++;
// }
// while (I >0&&I>=R) {
// if ((I-R) % (10*len) == 0) {
// return true;
// }
// I /= 10;
// }
// return false;
//}
int main() {
//设置一个数组inclu大小为:lenof(无重复序列R)*lenof(I)
//若I[j]中的数包含了R[i],则inclu[i][j]=1,否则为0
//输出inclu数组中1的个数,然后从依次按行输出R[i],i行中inclu[i][j]=1对应的j、I[j];
int lenI, lenR;
while (cin >> lenI) {
vector<int> I;
for (int i = 0; i < lenI; i++) {
int temp;
cin >> temp;
I.emplace_back(temp);
}
//R序列里的元素去重并按照元素大小从小到大排序
vector<int> R;
cin >> lenR;
for (int i = 0; i < lenR; i++) {
int temp;
cin >> temp;
bool flag = false;
for (int j = 0; j < R.size(); j++) {
if (temp == R[j]) {
flag = true;
break;
}
}
if (!flag) R.emplace_back(temp);
}
sort(R.begin(), R.end());
vector<vector<int>> inclu(R.size(), vector<int>(I.size()));
vector<int> sum(R.size());
for (int i = 0; i < R.size(); i++) {
for (int j = 0; j < I.size(); j++) {
if (match(R[i], I[j])) {
inclu[i][j] = 1;
sum[i]++;
}
}
}
int total = 0;
for (int i = 0; i < R.size(); i++) {
if (sum[i] != 0) {
total += 2;
total += 2 * sum[i];
}
}
cout << total << " ";
for (int i = 0; i < R.size(); i++) {
if (sum[i] != 0) {
cout << R[i] << " " << sum[i] << " ";
for (int j = 0; j < I.size(); j++) {
if (inclu[i][j] == 1) {
cout << j << " " << I[j] << " ";
}
}
}
}
cout<<endl;
}
return 0;
}
2、对输入的字符串进行加解密,并输出。
加密方法为:
当内容是英文字母时则用该英文字母的后一个字母替换,同时字母变换大小写,如字母a时则替换为B;字母Z时则替换为a;
当内容是数字时则把该数字加1,如0替换1,1替换2,9替换0;
其他字符不做变化。
解密方法为加密的逆过程。
收获:注意细节,在解密的时候与加密过程相反,所以特殊字符就变成了’a’,‘A’,‘0’,一定要细致,还有一个就是输入输出的,要记得多组输入!!!
#include<iostream>
#include<string>
#include<vector>
using namespace std;
void Encrypt(string password) {
vector<char> result(password.size(), '\0');
for (int i = 0; i < password.size(); i++) {
if (password[i] == 'z') result[i] = 'A';
else if (password[i] == 'Z') result[i] = 'a';
else if (password[i] == '9') result[i] = '0';
else if (password[i] >= 'a' && password[i] < 'z') result[i] = toupper(password[i] + 1);
else if (password[i] >= 'A' && password[i] < 'Z') result[i] = tolower(password[i] + 1);
else if (password[i] >= '0' && password[i] < '9') result[i] = password[i]+1;
//要记得处理不是数字和字符的元素
else result[i]=password[i];
}
string str;
str.assign(result.begin(), result.end());
cout << str << endl;
}
void Decrypt(string result) {
vector<char> password(result.size(), '\0');
for (int i = 0; i < result.size(); i++) {
if (result[i] == 'a') password[i] = 'Z';
else if (result[i] == 'A') password[i] = 'z';
else if (result[i] == '0') password[i] = '9';
else if (result[i] > 'a' && result[i] <= 'z') password[i] = toupper(result[i] - 1);
else if (result[i] > 'A' && result[i] <= 'Z') password[i] = tolower(result[i] - 1);
else if (result[i] > '0' && result[i] <= '9') password[i] = result[i]-1;
else password[i]=result[i];
}
string str;
str.assign(password.begin(), password.end());
cout << str<< endl;
}
int main() {
string password;
string result;
while(getline(cin,password)){
getline(cin,result);
Encrypt(password);
Decrypt(result);
}
return 0;
}
3、按照指定规则对输入的字符串进行处理。
详细描述:将输入的两个字符串合并。对合并后的字符串进行排序,要求为:下标为奇数的字符和下标为偶数的字符分别从小到大排序。这里的下标意思是字符在字符串中的位置。对排序后的字符串进行操作,如果字符为‘0’——‘9’或者‘A’——‘F’或者‘a’——‘f’,则对他们所代表的16进制的数进行BIT倒序的操作,并转换为相应的大写字符。如字符为‘4’,为0100b,则翻转后为0010b,也就是2。转换后的字符为‘2’; 如字符为‘7’,为0111b,则翻转后为1110b,也就是e。转换后的字符为大写‘E’。
举例:输入str1为"dec",str2为"fab",合并为“decfab”,分别对“dca”和“efb”进行排序,排序后为“abcedf”,转换后为“5D37BF”
接口设计及说明:
/功能:字符串处理
输入:两个字符串,需要异常处理
输出:合并处理后的字符串,具体要求参考文档
返回:无/
收获:
1、在程序中2^i是计算2异或i,不是计算2的i次幂;
2、就本题中在分离奇偶序列的时候,因为两个序列的长度不一定相同,所以如果分离的时候直接奇偶赋值的话,会出现越界的情况,这个时候可以在分离之前将两个子序列清空之后再使用+;
3、一定要注意容器如果没有初始化大小是不能直接赋初值的;
4、size代表容器里面所存元素的数量,capacity代表容器可容纳的数量。
- clear()将size置0,但是capacity不会改变;
- swap()的用法
1) vector1.swap(vector2); 将vector1和vector2的size、capacity一起交换;
2)vector().swap(v);将v的size和capacity一起清0; - v.clear(); v.shrink_to_fit(); 也可以达到将size和capacity一起清0的目的。
如果在整个程序中都需要使用到size,但是中途会对容器进行增删操作,最好先将size的值赋给一个整数,以免size更改之后导致程序错误。同时局部循环中使用的变量要考虑好是整个循环只需要赋一次初值还是每次循环都需要赋初值,以此来决定变量的位置(在循环中还是在循环外)
#include<iostream>
#include<string>
#include<algorithm>
#include<vector>
#include<cmath>
using namespace std;
//将十六进制数进行BIT倒序
int reverse_num(int num) {
vector<int> temp;
int result = 0;
if (num == 0) return 0;
while (num != 0) {
temp.emplace_back(num % 2);
num /= 2;
}
//例如0001,若不补全0的话,倒置之后还是1
while (temp.size() < 4) temp.emplace_back(0);
for (int i = temp.size() - 1; i >= 0; i--) {
//在程序中2^i是计算2异或i,不是计算2的i次幂
result += temp[i] * (pow(2, temp.size() - 1 - i));
}
return result;
}
void ProcessString(string str1, string str2) {
string result;
//合并
result = str1 + str2;
str1.clear();
str2.clear();
int len = result.size();
//分奇偶两个子序列
for (int i = 0; i < len; i++) {
if (i % 2 != 0) {
str1 += result[i];
}
else {
str2 += result[i];
}
}
//两个子序列分别排序
sort(str1.begin(), str1.end());
sort(str2.begin(), str2.end());
result.clear();
//排序之后合并
for (int i = 0, j = 0, k = 0; i < len; i++) {
if (i % 2 != 0) {
result += str1[j++];
}
else {
result += str2[k++];
}
}
//转换
for (int i = 0; i < len; i++) {
//暂存转换之后的数(int)
int reverse_temp = -1;
if (result[i] >= '0' && result[i] <= '9') {
reverse_temp = reverse_num(result[i] - 48);
}
else if (result[i] >= 'A' && result[i] <= 'F') {
reverse_temp = reverse_num(result[i] - 55);
}
else if (result[i] >= 'a' && result[i] <= 'f') {
reverse_temp = reverse_num(result[i] - 87);
}
//未处理非0-f的数,需要将reverse_temp初始值设为-1,不然会直接转换为0
if (reverse_temp >= 0 && reverse_temp <= 9) result[i] = char(reverse_temp + 48);
//转换为十六进制中的大写A-F
else if (reverse_temp > 9 && reverse_temp <= 15) result[i] = char(reverse_temp + 55);
}
cout << result << endl;
}
int main() {
string str1;
string str2;
while (cin >> str1) {
cin >> str2;
ProcessString(str1, str2);
}
}
4、素数伴侣
若两个正整数的和为素数,则这两个正整数称之为“素数伴侣”,如2和5、6和13,它们能应用于通信加密。现在密码学会请你设计一个程序,从已有的N(N为偶数)个正整数中挑选出若干对组成“素数伴侣”,挑选方案多种多样,例如有4个正整数:2,5,6,13,如果将5和6分为一组中只能得到一组“素数伴侣”,而将2和5、6和13编组将得到两组“素数伴侣”,能组成“素数伴侣”最多的方案称为“最佳方案”,当然密码学会希望你寻找出“最佳方案”。
输入: 有一个正偶数N(N≤100),表示待挑选的自然数的个数。后面给出具体的数字,范围为[2,30000]。
输出: 输出一个整数K,表示你求得的“最佳方案”组成“素数伴侣”的对数。
收获:
- 取整
(1)floor()的英文含义是地板,所以是向下取整,方便记忆叫它地板函数。会取不大于自变量的最大整数,这样自变量是3.1或3.9是没有区别的,返回都是3;自变量是-2.1或-2.9也是没有区别的,返回都是-3;
(2)ceil()的英文含义天花板,所以是向上取整,它就是天花板函数。会取不小于自变量的最大整数,这样自变量是3.1或3.9,返回都是4;自变量是-2.1或-2.9,返回的都是-2;
(3)round()的英文含义周围,环绕,这个就是常用的四舍五入函数,因为它会返回离自变量最近的整数,这个返回的整数可能大于也可能小于原来的数,但是一定是离它最近的那个整数,比如12.5返回13,12.4返回12。
还有一种四舍五入的方法 (int)(m +0.5)。 - vector初始化
(1)vector v(n,i)形式,v包含n 个值为 i 的元素
(2)vector v(v1)形式,v是v1 的一个副本
(3)vector v(n)形式,v包含n 个值初始化的元素
(4)//数组初始化vector
int iarray[]={1,2,3,4,5,6,7,8,9,0};
//count: iarray数组个数
size_t count=sizeof(iarray)/sizeof(int);
//int数组初始化 ivec3
vector ivec3(iarray,iarray+count);
(5) C++ Reference对于memset的定义为:void * memset ( void * ptr, int value, size_t num );
对int类型的内存区域进行初始化,ptr的类型是int*,举例说明一下:
1) 将内存初始化为0 :memset(ptr,0,sizeof(ptr));
2) 将内存初始化为-1:memset(ptr,0xff,sizeof(ptr));
因为按照字节进行填充,那么value值为0xff,将会把内存的每个字节都填充为0xff,则int值的区域为全1,那么int值就是-1。
3)将内存初始化为极大值:memset(ptr,0x3f,sizeof(ptr));
这段初始化代码初始出来的int值大小是1061109567
4)使用memset初始化vector
vector vec(10,1);
memset(vec.data(),0,vec.size()*sizeof(int));
5、对字符串中的所有单词进行倒排。
说明:1、构成单词的字符只有26个大写或小写英文字母;2、非构成单词的字符均视为单词间隔符;3、要求倒排后的单词间隔符以一个空格表示;如果原字符串中相邻单词间有多个间隔符时,倒排转换后也只允许出现一个空格间隔符;4、每个单词最长20个字母;
收获:map的三种元素插入方式
map<int,string> student;
student.insert(map<int,string>::value_type(1,“liMing”));
student.insert(pair<int,string>(2,“wanglei”));
student[3] = “pang”;
仔细阅读题目要求,非构成单词的字符均视为单词间隔
要注意,在循环输出单词的时候,如果直接用迭代器会导致最后一个单词后面多输出了一个空格,最终导致格式错误
#include<iostream>
#include<string>
#include<vector>
using namespace std;
//!!!非构成单词的字符均视为单词间隔符;
int main() {
string str;
while (getline(cin, str)) {
for(int i=0;i<str.size();i++){
if(str[i]<'A'||(str[i]>'Z'&&str[i]<'a')||str[i]>'z')
str[i]=' ';
}
vector<string> vstr;
int i = 0;
while (i < str.size()) {
int len = 0;
while (i < str.size() && str[i] != ' ') {
len++;
i++;
}
if(len!=0) vstr.emplace_back(str.substr(i - len, len));
i += 1;
}
if(vstr.size()==0) cout<<endl;
for(int i=vstr.size()-1;i>0;i--){
cout<<vstr[i]<<" ";
}
cout <<vstr[0]<< endl;
}
}
6、密码截取(运行内存超限?)
Catcher是MCA国的情报员,他工作时发现敌国会用一些对称的密码进行通信,比如像这些ABBA,ABA,A,123321,但是他们有时会在开始或结束时加入一些无关的字符以防止别国破解。比如进行下列变化 ABBA->12ABBA,ABA->ABAKK,123321->51233214 。因为截获的串太长了,而且存在多种可能的情况(abaaab可看作是aba,或baaab的加密形式),Cathcer的工作量实在是太大了,他只能向电脑高手求助,你能帮Catcher找出最长的有效密码串吗?
输入一个字符串,返回有效密码串的最大长度。
7、整数与IP地址间转换
收获:最常见的误用应该就是pow(int,int),在某些编译器里可以通过,但在其他环境里会报错在C++中,pow()有多个重载函数,可以发现是没有pow(int,int)的:
一般来说的正确用法是pow(int,double),如:pow(2,2.0) 也就是把指数化成浮点数形式!
同样的,sqrt的原型也是:
double sqrt ( double x );
float sqrt ( float x );
long double sqrt ( long double x );
#include<iostream>
#include<string>
#include<stack>
#include<cmath>
using namespace std;
long IPtoString(string IP) {
long temp = 0;
int i = IP.size() - 1;
double times = 0;
while (i >= 0) {
int len = 0;
while (i >= 0 && IP[i] != '.')
{
i--;
len++;
}
int t = stoi(IP.substr(i + 1, len));
temp += t*pow(256, times);
times++;
i--;
}
return temp;
}
string StringtoIP(long str) {
stack<int> temp;
while (str > 0) {
temp.push(str % 256);
str /= 256;
}
//注意如果是str=1的话,需要补全0,不然输出的话就是1而不是0.0.0.1
while (temp.size() < 4) {
temp.push(0);
}
string IP;
while (!temp.empty()) {
IP += to_string(temp.top()) + '.';
temp.pop();
}
return IP.substr(0, IP.size() - 1);
}
int main() {
string IP;
long str;
while (cin >> IP) {
cin >> str;
printf("%ld",IPtoString(IP));
cout << endl;
cout << StringtoIP(str)<< endl;
}
return 0;
}
8、求最大公约数和最小公倍数
#include<iostream>
using namespace std;
//求最大公约数
int gcd(int a,int b){
while(a%b){
int temp=a;
a=b;
b=temp%b;
}
return b;
}
int main(){
int a,b;
while(cin>>a>>b){
cout<<a*b/gcd(a,b)<<endl;
}
return 0;
}
9、求立方根
令f(x) = x^3 - a,求解f(x) = x^3 - a = 0。
利用泰勒公式展开,即f(x)在xo处的函数值为:
f(x) = f(xo) +f’(xo)(x-xo) = xo3-a+3xo2(x-x0) = 0,
解之得:x = xo - (xo^3 - a) / (3xo^2)。
求平方根用一个套路@_@:
令f(x) = x^2 - a,求解f(x) = x^2 - a = 0。
利用泰勒公式展开,即f(x)在xo处的函数值为:
f(x) = f(xo) +f’(xo)(x-xo) = xo^2-a+2xo(x-x0) = 0,
解之得:x = (x+a/xo) / 2。
cout<<showpoint<<sum<<endl; 可以输出浮点数
#include<iostream>
#include<cmath>
using namespace std;
double fun(double num){
double x=1.0;
while(fabs(x*x*x-num)>1e-9){
x=x-((x*x*x-num)/(3*x*x));
}
return x;
}
int main(){
int num;
while(cin>>num){
double ans=fun(num);
printf("%.1f",ans);
}
return 0;
}
10、迷宫问题(记得要回看)
定义一个二维数组N*M(其中2<=N<=10;2<=M<=10),如5 × 5数组下所示: int maze[5][5] = {{0, 1, 0, 0, 0},{0, 1, 0, 1, 0},{ 0, 0, 0, 0, 0},{ 0,1, 1, 1, 0},{0, 0, 0, 1, 0}};它表示一个迷宫,其中的1表示墙壁,0表示可以走的路,只能横着走或竖着走,不能斜着走,要求编程序找出从左上角到右下角的最短路线。入口点为[0,0],既第一空格是可以走的路。
Input:一个N × M的二维数组,表示一个迷宫。数据保证有唯一解,不考虑有多解的情况,即迷宫只有一条通道。
Output:左上角到右下角的最短路径。
收获:这个与动态规划不同,迷宫问题可以往四个方向走,且题中说明了:数据保证有唯一解。此题应该用回溯。
#include<iostream>
#include<vector>
//使用回溯法
using namespace std;
int N, M;
vector<vector<int>> maze;
//存储当前路径,第一维表示位置
vector<vector<int>> path_temp;
vector<vector<int>> path_best;
void MazeTrack(int i, int j) {
//当前节点已走,不可再走
maze[i][j] = 1;
path_temp.push_back({ i,j });
if (i == N - 1 && j == M - 1)
if (path_best.empty() || path_temp.size() < path_best.size())
path_best = path_temp;
//向上走
if (i - 1 >= 0 && maze[i - 1][j] == 0)
MazeTrack(i - 1, j);
//向下走
if (i + 1 < N && maze[i + 1][j] == 0)
MazeTrack(i + 1, j);
//向左走
if (j - 1 >= 0 && maze[i][j - 1] == 0)
MazeTrack(i, j - 1);
//向右走
if (j + 1 < M && maze[i][j + 1] == 0)
MazeTrack(i, j + 1);
//恢复为未走
maze[i][j] = 0;
path_temp.pop_back();
}
int main() {
while (cin >> N >> M) {
maze = vector<vector<int>>(N, vector<int>(M, 0));
path_temp.clear();
path_best.clear();
for (auto& i : maze)
for (auto& j : i)
cin >> j;
MazeTrack(0, 0);
for (auto i : path_best)
cout << '(' << i[0] << ',' << i[1] << ')' << endl;
}
return 0;
}
补充知识1:find函数的用法
1、algorithm中的find()函数。返回值是目标元素的下标,找不到时返回值为迭代器结尾
2、string中的与find相关函数。
- find();
string str1, str2;
char c;
str1.find(str2);//从串str1中查找时str2,返回str2中首个字符在str1中的地址
str1.find(str2,5);//从str1的第5个字符开始查找str2
str1.find(c);//在str1中查找字符c并返回第一个查找到的地址
str1.find("str2",2 , 2);//从str1中的第二个字符开始查找of big的前两个字符
- find_first_of()
函数原型:int find_first_of(char c, int start = 0);
这个用法和1)中str1.find(str2)相似,都是返回str2中首个字符在str1中的地址。但是要特别注意,没有找到时返回值是-1. - find_last_of()
函数原型:int find_last_of(char c);从源字符串起始位置pos(默认为npos)处,依此查找每个字符。如果某字符等于目标字符c,则返回首次匹配的该字符的位置。
未找到时返回-1。 - find_not_first_of()
函数原型:size_type find_first_not_of( char ch, size_type index = 0 );
在字符串中查找第一个与str中的字符都不匹配的字符,返回它的位置。搜索从index开始。如果没找到就返回string::nops。 - find_not_last_of()
函数原型:size_type find_last_not_of( char ch, size_type index = 0 );
在字符串中查找最后一个与str中的字符都不匹配的字符,返回它的位置。搜索从index开始。如果没找到就返回string::nops。
补充知识2:printf输出格式
#include<stdio.h>
#include<string.h>
int main()
{
char c, s[20];
int a=1234;
float f=3.141592653589;
double x=0.12345678912345678;
strcpy(s, "Hello,World");
c='\x41';
printf("a=%d\n", a);//按照十进制整数格式输出,显示 a=1234
printf("a=%d%%\n", a);//输出%号 结果 a=1234%
printf("a=%6d\n", a);//输出6位十进制整数 左边补空格,显示 a= 1234
printf("a=%06d\n", a);//输出6位十进制整数 左边补0,显示 a=001234
printf("a=%2d\n", a);//a超过2位,按实际输出 a=1234
printf("a=%-6d\n", a);///输出6位十进制整数 右边补空格,显示 a=1234
printf("f=%f\n", f);//浮点数有效数字是7位,结果 f=3.141593
printf("f=6.4f\n", f);//输出6列,小数点后4位,结果 f=3.1416
printf("x=%lf\n", x);//输出长浮点数 x=0.123457
printf("x=%18.16lf\n", x);//输出18列,小数点后16位,x=0.1234567891234567
printf("c=%c\n", c); //输出字符 c=A
printf("c=%x\n", c);//以十六进制输出字符的ASCII码 c=41
printf("s[]=%s\n", s);//输出数组字符串s[]=Hello,World
printf("s[]=%6.9s\n", s);//输出最多9个字符的字符串 s[]=Hello,Wor
return 0;
}