codeup字符串处理problem A 1983

题目描述

读入两个字符串,字符串除了数字还可能包括 ‘—’、‘E’、‘e’、’.’,相加之后输出结果,如果是浮点型,要求用科学计数法表示(最多包含10个有效数字)。

输入

输入包含多组测试数据。

每组输入占两行,每行一个字符串,测试数据保证字符串的构成严格按照题目中的描述。

输出

输出两个数字相加的结果,每组输出占一行。

样例输入

34.56
2.45e2

样例输出

2.7956e2

注意事项

这道题的描述不是很清楚,负号“-”是在哪里没有说明,1、负号可能出现在首端,表示这是一个负数;2、也可能出现在“e”的后面,表示10的负几次方。所以这道题我刚看的时候觉得有点棘手,一开始我个人是考虑2的情况的。

当时的思路是:

  1. 根据 出现“.”、“e”之类的情况将字符串转换为数值型表示(不含科学计数法,就是把2.5e3转换成2500);
  2. 然后两个数相加;
  3. 将相加后的数输出。

但是,当时通过这么一个思路遇到了一些问题,就是对于小数,要保留多少位来输出没法来确定,遂夭折,但此时我仍然在意着这个负号到底是属于哪种情况?

于是,在自己的思路不行的情况下我在网上看了看其他人的博客,感谢子夜葵分享的代码,代码在codeup上提交成功,但该博客内的代码没有注释,所以自己在搞明白之后对代码做了部分分析与注释,写出来跟大家分享一下。

原代码

#include
int main()
{
    char str1[50],str2[50];
    long long s,s1,s2,ans;
    int i,a1,a2,a,b,c,w,flag;
    while(scanf("%s %s",str1,str2)!=EOF){
        s1=s2=flag=b=c=a1=0;
        for(i=0;str1[i];i++){
            if(str1[i]=='-')
                flag=1;
            else if(str1[i]=='.')
                c=1;
            else if(str1[i]=='e'||str1[i]=='E'){
                sscanf(str1+i+1,"%d",&b);
                a1+=b;
                break;
            }
            else{
                s1=s1*10+str1[i]-'0';
                a1-=c;
            }
        }
        if(flag) s1=-s1;
        flag=b=c=a2=0;
        for(i=0;str2[i];i++){
            if(str2[i]=='-')
                flag=1;
            else if(str2[i]=='.')
                c=1;
            else if(str2[i]=='e'||str2[i]=='E'){
                sscanf(str2+i+1,"%d",&b);
                a2+=b;
                break;
            }
            else{
                s2=s2*10+str2[i]-'0';
                a2-=c;
            }
        }
        if(flag) s2=-s2;
        if(a1<a2)
            for(;a1<a2;a2--)
                s2*=10;
        else if(a1>a2)
            for(;a1>a2;a1--)
                s1*=10;
        a=a1;s=s1+s2;
        if(!s){
            printf("0\n");
            continue;
        }
        while(a<0&&s%10==0){
            s/=10;
            a++;
        }
        if(a>=0){
            printf("%lld",s);
            for(i=0;i<a;i++)
                printf("0");
            printf("\n");
            continue;
        }
        flag=0;
        if(s<0){
            s=-s;
            flag=1;
        }
        ans=1;w=0;
        while(ans<=s){
            ans*=10;
            w++;
        }
        if(ans>1){
            ans/=10;
			w--;
        }
        if(flag)
            printf("-");
        printf("%lld",s/ans);
        if(ans>1)
            printf(".%lld",s%ans);
        printf("e%d\n",a+w);
    }
    return 0;
}

原作者思路

根据对代码的分析,个人整理出来的思路是这样的:
首先,负号是我上述的第1种情况,即只用于表示正负数;
然后,将字符串转换为数值,这个地方跟我的思路不同,下面会详细说;
最后,相加,根据相加的结果来判断以什么样的方式输出。

代码分析

char str1[50],str2[50];
long long s,s1,s2,ans;
int i,a1,a2,a,b,c,w,flag;

1、先定义了一堆变量,我们先不管。

for(i=0;str1[i];i++){
	if(str1[i]=='-')//如果有负号
		flag=1;//flag是判断该数是不是负数
	else if(str1[i]=='.')//如果是小数
        c=1;
    else if(str1[i]=='e'||str1[i]=='E'){
        sscanf(str1+i+1,"%d",&b);//如果有e,将e后面的数赋给b
        a1+=b;//a1是e的指数
        break;
    }else{
        s1=s1*10+str1[i]-'0';//s1存储这个数
        a1-=c;
    }
}
if(flag) s1=-s1;//如果e前有负号,s1反转,变为负数

2、第1个for循环,主要是什么意思呢?其实就是把字符串表示为数值型加科学计数法的方式。
这段代码的转换思路如下:
i从0开始到末尾,
如果第i个字符是‘-’,表明是负数,所以flag为1;
如果第i个字符是‘.’,令c为1(这里c=1,是用于下面转换为整型数值);
如果第i个字符是‘e’或‘E’,就把e后面的指数加到a1上去;
如果第i个字符是一个数,就令数不断该边,转换成数。
这里a1-=c的意思就是,一开始,c是0,那么当不遇到小数点的时候,这个整数在增大,a1也没变,当遇到小数点后,c变为1,没往后增加一位,就要让指数减1。
以 24.56e3 来举例

  1. i=0,str[0]=2,s1=2,a1=0,c=0,b=0;
  2. i=1,str[1]=4,s1=24,a1=0,c=0,b=0;
  3. i=2,str[2]=’.’,s1=24,a1=0,c=1,b=0;
  4. i=3,str[3]=5,s1=245,a1=-1,c=1,b=0;
  5. i=4,str[4]=6,s1=2456,a1=-2,c=1,b=0;
  6. i=5,str[5]=‘e’,s1=2456,a1=1,c=1,b=3;

这里就是把24.56e3转换为2456e1

for(i=0;str2[i];i++){
	if(str2[i]=='-')//如果有负号
		flag=1;//flag是判断该数是不是负数
	else if(str2[i]=='.')//如果是小数
        c=1;
    else if(str2[i]=='e'||str2[i]=='E'){
        sscanf(str2+i+1,"%d",&b);//如果有e,将e后面的数赋给b
        a2+=b;//a2是e的指数
        break;
    }else{
        s2=s1*10+str2[i]-'0';//s2存储这个数
        a2-=c;
    }
}
if(flag) s2=-s2;//如果e前有负号,s2反转,变为负数

3、第2个for循环是对字符串str2的操作,与上面相同。

if(a1<a2){
	for(;a1<a2;a2--){
		s2*=10;//将10^a2-a1乘到s2上,转化为数值,来统一两个数的e的指数
    }
}else if(a1>a2){
    for(;a1>a2;a1--){
        s1*=10;
    }
}
a=a1;//a为相加后的e的指数
s=s1+s2;//数值部分相加

4、这部分就是统一两个数字的指数
比如一个是2456e1,一个是3452e2,就把第二个转换为34520e1。然后相加,将前面的数值赋给s,后面的指数赋给a。

if(!s){//若s为0
	printf("0\n");
    continue;
}
while(a<0&&s%10==0){//若数值类似于1000且后面有e^a次方,可以改一下
    s/=10;
    a++;
}
if(a>=0){//若a>0,则化为整数
    printf("%lld",s);
    for(i=0;i<a;i++){
    	printf("0");
    }
    printf("\n");
    continue;
}
flag=0;
if(s<0){//s为负数
    s=-s;
    flag=1;
}
ans=1;
w=0;//w为s的位数
while(ans<=s){
    ans*=10;//ans是对应位数的整十数,如1000
    w++;
}
if(ans>1){//ans倒退一位,用于后边小数点后面的输出
    ans/=10;
    w--;
}
if(flag){//s<0
    printf("-");
}
printf("%lld",s/ans);
if(ans>1){
    printf(".%lld",s%ans);
}
printf("e%d\n",a+w);

5、这一部分就是输出:
1、如果s为0,说明结果为0,直接输出0;
2、然后将整型改一下,比如2500e2改为25e4;
3、如果a>0,说明是个整数,所以不用科学计数法,输出;
4、下面负数与整数输入方式一样,就是负数前面输一个’-'号,
以2456e1举例:
首先用w记录2456的位数,即w=3,
然后根据w给一个1000的数ans,
用2456/1000得到2,输出”2.“,
2456%1000=456,输出”456“,
再接着输出”e“和”a+w“。即输出"e4",
最终输出2.456e4。

总结

因此整个代码的思路就是这样,跟我一开始的思路相比,
1、用整型和e来表示这个数,而我一开始是想直接用一个double类型的数来表示,这样就避免了我之前遇到的输出位数不确定是多少的情况。比如1.234567891,用%.lf就是默认保留6位小数。
2、通过分析这个代码我们知道这道题的负号是用于正负的,而非e的负多少次方,为此我验证过,当输入1和1.245e-2时,输出是1.1245e0,这显然是不对的。

目前就这么多,写得不怎么好,大家将就着看吧。

你可能感兴趣的:(算法,字符串,codeup,两数相加)