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