C++实现科学计数法(PAT习题)--字符串,string容器

C++字符串(string)实现科学计数法

在跟随《算法笔记》刷PAT时,多次遇到科学计数法相关的题目B1024,A1060,A1073做题过程中均耗费了大量的时间,因此对该类型题目做些总结

此类型题目属于字符串处理,string容器的使用较复杂的模拟问题,无需使用复杂算法,但需要考虑的边界,细节较多

思路方向:

此类题目类型为:数字转换为科学计数法,科学计数法转换为数
一般将此类题目数据分为,指数和底数两个部分,分别进行处理。其中小数点是这类题目的关键点

几点注意:

1、底数正负号问题:较为简单,一般不需要太多处理,直接写在最前面即可
2、指数正负号问题:尤其是符号需要考虑纯小数问题,即小数点后紧跟着的0的个数
3、指数大小问题:与精确的位数,和小数点前的位数有关
4、底数大小问题:与指数比较后涉及补0的问题
5、小数点问题:是处理该类问题的关键,小数点前面的位数与指数有关,小数点后的位数与精确的位数有关
6、补0问题:当有效位数不足精确位数时,需要补0,指数的正负不同,补0的位置也有所不同,指数为正数在末位补0,为负数在小数点后补0
7、前导零问题要注意!由于此题大多是字符串输入,可能会有以0开头,或以末位若干个0表示高精度的字符串出现;数字0的转换要考虑到位,处理到位

B1024/A1073科学计数法

本人做了两遍但都花费了很长的时间,这道题我认为逻辑比较复杂,细节处理的地方很多(指数与底数的符号问题,小数点问题)。当然本题的关键就是指数大小与小数点的位数比较问题

思路

根据指数符号分为3类,0,正,负
0:原样输出
正:指数大于等于小数点后的位数时,先写数再补零;小于时先写数,点小数点,再写数
负:先补零,再写数
题目不再赘述

代码

#include <bits/stdc++.h>
using namespace std;
char str[10005];
char nums[10005], ans[10005]; 
char exps[10];
int main(int argc, char *argv[]) {
	cin >> str;
	int len = strlen(str);
	int start = 0;
	if(str[0] == '-')
	{
		printf("%c", str[0]);
		start++;
	}
	//拆分 
	bool flag = false;//false表示底数
	int lenNums = 0, lenExps = 0; 
	for(int i = start; i < len; i++)
	{
		if(str[i] == 'E')
		{
			flag = true;
		}
		else if(flag == false)//底数阶段 
		{
			if(str[i] != '+')
				nums[lenNums++] = str[i];//含小数点 
		}
		else if(flag == true)//指数阶段,带正负号 
		{
			exps[lenExps++] = str[i];
		}
	}
	int index = 0;//记录ans数组
	int ex = 0;//指数的值
	int actCnt = lenNums - 2;//实际小数点后的位数 
	sscanf(exps, "%d", &ex); 
	if(ex == 0)//指数是0次幂 
	{
		for(int i = 0; i < lenNums; i++)
		{
			ans[index++] = nums[i];//若为底数为正数,nums[0]是'+'
		}
	}
	else if(exps[0] == '+')//指数为正 
	{
		//对底数操作 
		ans[index++] = nums[0];//处理小数点前的一位 
		if(ex >= actCnt)//指数大于等于小数点后的位数 
		{
			for(int i = 2; i < actCnt + 2; i++)//i从小数点后的一位开始 
			{
				ans[index++] = nums[i]; 
			}
			for(int i = 0; i < ex - actCnt; i++)//多出来的补零 
			{
				ans[index++] = '0';
			}
		}
		else//指数小于后面的位数,要点小数点 
		{
			for(int i = 2; i < ex + 2; i++)//先写数 
			{
				ans[index++] = nums[i];
			} 
			ans[index++] = '.';
			for(int i = ex + 2; i < lenNums; i++)
			{
				ans[index++] = nums[i];
			}
		}
	} 
	
	else if(exps[0] == '-')//指数为负 
	{
		ex = -ex;//取相反数 
		ans[index++] = '0';
		ans[index++] = '.';
		if(ex > 1)
		{
			for(int j = 0; j < ex - 1; j++)
			{
				ans[index++] = '0';
			}
		}
		for(int i = 0; i < lenNums; i++)
		{
			if(nums[i] != '+' && nums[i] != '.')
			{
				ans[index++] = nums[i];
			}	
		}
	}
	//输出
	if(str[0] == '0' || str[1] == '0')
	{
		printf("0");
	} 
	else
	{
		for(int i = 0; i < index; i++)
		{
			printf("%c", ans[i]);
		}
	}	
	return 0;
}

A1060.Are They Equal:

此题可使用string容器解答
题目链接

个人解题时的思路:

个人最初的思路:不断进行字符串拼接(可能是因为考虑使用string容器,先入为主),发现未考虑,小数点和指数,底数
随后转变思路:要比较两个字符串,则可通过比较二者指数和底数是否相等即可,指数与小数点前的位数有关,底数与有效数字的个数和精确到的位数有关,发现未考虑,前导0(字符串以0打头),后导0(小数点后末位含一串0),负指数(纯小数,小数点后先是一串0,随后是非零数)

解题思路:

抹去前导0,后导0,小数点,以及统计小数点后(第一个非0数前)是否有0,过程中计算指数,保留有效。当抹去各种0和小数点后,剩下的均为非0的有效数字,再与精确的位数作比较,当有效数字小于精确位数时,末位补0

代码:

#include <bits/stdc++.h>
using namespace std;
int N;
string A, B;
int expA, expB;
string preProcess(string s, int &exp)
{
	int index = 0;
	while(s.length() > 0 && s[0] == '0')
	{
		s.erase(0, 1);//去除前导0 
	}
	if(s[0] == '.')//第一位是小数点,原数是小于1的数字 
	{
		s.erase(0, 1);//去除小数点
		while(s.length() > 0 && s[0] == '0')//小数点后的0 
		{
			exp--;
			s.erase(0, 1);
		} 
	}
	else//原数大于1,寻找小数点 
	{
		while(index < s.length() && s[index] != '.')
		{
			index++;
			exp++;
		}
		if(index < s.length())//小数点在原数中间,说明原数有小数部分 
		{
			s.erase(index, 1);
		}//否则原数即为整数 
	} 
	if(s.length() == 0)//除去前导0之后,长度为0,则原数即为0 
	{
		exp = 0;
	} 
	int validnum = 0;
	index = 0;
	string res;
	while(validnum < N)
	{
		if(index < s.length())
		{
			res += s[index++];
		}
		else
		{
			res += '0';
		}
		validnum++;
	}
	return res;
}
int main(int argc, char *argv[]) {
	cin >> N >> A >> B;
	string resA = preProcess(A, expA);
	string resB = preProcess(B, expB);
	if(resA == resB && expA == expB)
	{
		cout << "YES 0." << resA << "*10^" << expA;
	}
	else
	{
		cout << "NO 0." << resA << "*10^" << expA << " 0." << resB << "*10^" << expB;
	}
	return 0;
}
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值