您的位置:首页 > 其它

华为OJ之正数相减

2015-05-31 14:59 169 查看

题目描述:

请编写程序实现:

两个任意长度的正数相减,这两个正数可以带小数点,也可以是整数,请输出结果.

系统假设:

输入的字符串中,不会出现除了数字与小数点以外的其它字符,不会出现多个小数点以及小数点在第一个字符的位置

等非法情况,所以考生的程序中无须考虑输入的数值字符串非法的情况。

详细要求以及系统约束

1.输入均为正数,但输出可能为负数

2.输入输出均为字符串形式,输入的字符串以“\0”结束,输出的结果字符串也必须以“\0”结束

3.如果输出是正数则不需要带符号,如果为负数,则输出的结果字符串需要带负号

例如:2.2-1.1 直接输出为“1.1”,1.1-2.2 则需要输出为“-1.1”

4.输出的结果字符串需要过滤掉整数位前以及小数位后无效的0,小数位为全0的,直接输出整数位

例如相减结果为11.345,此数值前后均不可以带0,“011.345”或者“0011.34500”等等前后带无效0的均视为错误

输出。例如1.1-1.1结果为0.0,则直接输出0。

5.输出的字符串内存由考生的程序负责分配,测试程序负责释放

本题出自高级篇,该题看起来意思简单,实则要注意的地方有很多,特别是标红的地方。简要说说该题的几点注意事项:

由于长度任意,因此想把字符串转换为数字后再计算的想法就被枪毙了;
涉及到小数运算,对齐借位的问题更加麻烦;
得到结果字符串中“0”的处理。

先来几个测试用例,如果顺利通过了,那提交正确的概率就很大了:

// 用例1:小数点+前0+负数结果
void CExampleTest::TestCase01()
{
const char * pMinuend = "01.";
const char * pSubtrahend = "2.";
char * pResult = NULL;
Decrease(pMinuend, pSubtrahend, &pResult);
CPPUNIT_ASSERT(pResult != NULL);
CPPUNIT_ASSERT(strcmp(pResult, "-1") == 0);
free(pResult);
}

// 用例2:位数测试
void CExampleTest::TestCase02()
{
const char * pMinuend = "8.1";
const char * pSubtrahend = "0.00000000000000000000001";
char * pResult = NULL;
Decrease(pMinuend, pSubtrahend, &pResult);
CPPUNIT_ASSERT(pResult != NULL);
CPPUNIT_ASSERT(strcmp(pResult, "8.09999999999999999999999") == 0);
free(pResult);
}

// 用例3:后0
void CExampleTest::TestCase03()
{
const char * pMinuend = "8.5";
const char * pSubtrahend = "7.5";
char * pResult = NULL;
Decrease(pMinuend, pSubtrahend, &pResult);
CPPUNIT_ASSERT(pResult != NULL);
CPPUNIT_ASSERT(strcmp(pResult, "1") == 0);
free(pResult);
}

// 用例4:负数+小数点
void CExampleTest::TestCase04()
{
const char * pMinuend = "12.34";
const char * pSubtrahend = "17.24";
char * pResult = NULL;
Decrease(pMinuend, pSubtrahend, &pResult);
CPPUNIT_ASSERT(pResult != NULL);
CPPUNIT_ASSERT(strcmp(pResult, "-4.9") == 0);
free(pResult);
}

// 用例5:整数+后0
void CExampleTest::TestCase05()
{
const char * pMinuend = "12.";
const char * pSubtrahend = "2";
char * pResult = NULL;
Decrease(pMinuend, pSubtrahend, &pResult);
CPPUNIT_ASSERT(pResult != NULL);
CPPUNIT_ASSERT(strcmp(pResult, "10") == 0);
free(pResult);
}


根据上述的分析可知,本题的思路为:

先判断结果是否为负,若为负则将标志位置位,转换为大数减小数,最后要将结果加上负号;
小数位对齐;
从最低位开始相减,不够则向前借位;
进行结果中“0”的处理,去掉整数部分头部的“0”和小数部分尾部的“0”;

最后的代码如下:

/******************************************************************************

Copyright (C), 2001-2011, Huawei Tech. Co., Ltd.

******************************************************************************
File Name     :
Version       :
Author        :
Created       : 2009/10/10
Last Modified :
Description   :
Function List :

History       :
1.Date        : 2009/10/10
Author      :
Modification: Created file

******************************************************************************/
#include <stdlib.h>
#include <string.h>

typedef struct MYNUM
{
char *pZhengshu;
char *pXiaoshu;
}MyNum;

/*获取整数部分和小数部分*/
int GetTwoParts(const char *pcStr,char **pcZhengshu,char **pcXiaoshu)
{
if (NULL == pcStr)
return 0;
char *pZhengshu = NULL;
char *pXiaoshu = NULL;
size_t nLen = strlen(pcStr);
int i = 0;
while ('\0' != pcStr[i])
{
if ('.' == pcStr[i])
break;
i++;
}
i++;
pZhengshu = (char *)malloc(i * sizeof (char));
if (NULL == pZhengshu)
return 0;
strncpy(pZhengshu,pcStr,i - 1);
pZhengshu[i - 1] = '\0';
*pcZhengshu = pZhengshu;
int nXiaoshuLen = 0;
if (nLen <= i)
nXiaoshuLen = 1;
else
nXiaoshuLen = nLen - i + 1;
pXiaoshu = (char *)malloc(nXiaoshuLen * sizeof (char));
if (NULL == pXiaoshu)
return 0;
if (nLen > i)
{
strncpy(pXiaoshu,&pcStr[i],(nLen - i));

}
pXiaoshu[nXiaoshuLen - 1] = '\0';
*pcXiaoshu = pXiaoshu;
return (i - 1);
}

/*获取整数部分位数*/
size_t GetIntNum(const char *pcStr)
{
size_t i = 0;
size_t nNum = 0;
bool bHeadZero = true;
while ('\0' != pcStr[i])
{
if ('0' != pcStr[i])
bHeadZero = false;
if ('.' == pcStr[i])
break;
if (!bHeadZero)
nNum++;
i++;
}
return nNum;
}

/*正数减法*/
void PosDecrease(const char *pMinuend,const char* pSubtrahend,char **pResult,bool bIsNeg)
{
bool bPointFlag = false;
int nLen1 = strlen(pMinuend);
int nLen2 = strlen(pSubtrahend);
if ('.' == pMinuend[nLen1 - 1])
nLen1--;
if ('.' == pSubtrahend[nLen2 - 1])
nLen2--;
char *pTem = NULL;
char *pMinTem = NULL;
pTem = (char *)malloc((nLen1 + 1) * sizeof (char));
pMinTem = (char*)malloc((nLen1 + 1) * sizeof (char));
if ((NULL == pTem) || (NULL == pMinTem))
return;
strncpy(pMinTem,pMinuend,nLen1);
pMinTem[nLen1] = '\0';
for (int i = 0;i < nLen1;i++)
{
if ('.' == pMinTem[nLen1 - i - 1])
{
pTem[nLen1 - 1 - i] = '.';
bPointFlag = true;
continue;
}
char cSub;
if (i >= nLen2)
cSub = '0';
else
cSub = pSubtrahend[nLen2 - i - 1];
char cTem = pMinTem[nLen1 - i - 1] - cSub;
if (0 > cTem)
{
for (int j = 0;j < nLen1 -i;j++)
{
if ('.' == pMinTem[nLen1 - i - 1 - j -1])
continue;
if ('0' > (pMinTem[nLen1- i - 1 - j - 1]--))
{
pMinTem[nLen1 - i - 1 - j - 1] += 10;
}
else
break;
}
cTem += 10;
}
cTem += '0';
pTem[nLen1 - 1 - i] = cTem;
}
pTem[nLen1] = '\0';
int nStart,nEnd;
for (nStart= 0;nStart< nLen1;nStart++)
{
if ('0' != pTem[nStart])
break;
}
for (nEnd = nLen1 - 1;nEnd >= 0;nEnd--)
{
if ('0' != pTem[nEnd])
break;
}
if ('.' == pTem[nStart])
nStart--;
if ('.' == pTem[nEnd])
nEnd--;
if (!bPointFlag)
nEnd = nLen1 - 1;
char *pLast = NULL;
if (nLen1 == nStart)
{
pLast = (char *)malloc(2 * sizeof (char));
if (NULL == pLast)
return;
strcpy(pLast,"0");
}
else
{
int nLen = nEnd - nStart + 1;
if (bIsNeg)
{
pLast = (char *)malloc((nLen + 2) * sizeof(char));
if (NULL == pLast)
return;
pLast[0] = '-';
strncpy(&pLast[1],&pTem[nStart],nLen);
pLast[nLen + 1] = '\0';
}
else
{
pLast = (char *)malloc((nLen+ 1) * sizeof(char));
strncpy(pLast,&pTem[nStart],nLen);
pLast[nLen] = '\0';
}
}
*pResult = pLast;
free(pTem);
free(pMinTem);
}

/**********************************************************
描述:  小数位对齐
输入:
const char *pStr 需要对齐的字符串
int nAlignLen  对齐后的位数
输出:
char **pAlign 对齐后的字符串,以'\0'结束
返回值:
0 成功
-1 失败

***********************************************************/
int XiaoshuAlign(const char *pStr,int nAlignLen,char **pAlign)
{
if (NULL == pStr)
return -1;
int nLen = strlen(pStr);
if (nLen > nAlignLen)
return -1;
bool bPointFlag = false;
int i = 0;
while ('\0' != pStr[i])
{
if ('.' == pStr[i])
{
bPointFlag = true;
break;
}
i++;
}
if (!bPointFlag)
nAlignLen++;
char *pAlignTem = NULL;
pAlignTem = (char*)malloc((nAlignLen + 1) * sizeof(char));
if (NULL == pAlignTem)
return -1;
strcpy(pAlignTem,pStr);
for (int i = nLen;i < nAlignLen;i++)
{
if (!bPointFlag && (i == nLen))
pAlignTem[i] = '.';
else
pAlignTem[i] = '0';
}
pAlignTem[nAlignLen] = '\0';
*pAlign = pAlignTem;
return 0;
}

/*****************************************************************************
Description  : 两个任意长度的正数相减
Prototype    : int Decrease(const char *pMinuend, const char *pSubtrahend, char **ppResult)
Input Param  : const char *pMinuend     被减数,以\0表示字符串结束
const char *pSubtrahend  减数,以\0表示字符串结束
Output       : char **ppResult          减法结果,必须以\0表示字符串结束
Return Value : 成功返回0   失败返回-1
*****************************************************************************/
int Decrease(const char *pMinuend, const char *pSubtrahend, char **ppResult)
{

/* 在这里实现功能 */
if ((NULL == pMinuend) || (NULL == pSubtrahend))
return -1;

char *pResult = NULL;

size_t n1,n2;
n1 = n2 = 0;
n1 = GetIntNum(pMinuend);
n2 = GetIntNum(pSubtrahend);

const char *cVar1 = NULL;
const char *cVar2 = NULL;
bool bIsNeg = false;
if (n1 > n2)
{
cVar1 = pMinuend;
cVar2 = pSubtrahend;
}
else if (n1 == n2)
{
if (0 <= strcmp(pMinuend,pSubtrahend))
{
cVar1 = pMinuend;
cVar2 = pSubtrahend;
}
else
{
cVar1 = pSubtrahend;
cVar2 = pMinuend;
bIsNeg = true;
}
}
else
{
cVar1 = pSubtrahend;
cVar2 = pMinuend;
bIsNeg = true;
}

size_t nLen1 = strlen(cVar1);
size_t nLen2 = strlen(cVar2);
MyNum m_Num1,m_Num2;
m_Num1.pZhengshu = m_Num1.pXiaoshu = m_Num2.pZhengshu = m_Num2.pXiaoshu = NULL;
GetTwoParts(cVar1,&(m_Num1.pZhengshu), &(m_Num1.pXiaoshu));
GetTwoParts(cVar2,&(m_Num2.pZhengshu),&(m_Num2.pXiaoshu));

int nXiaoshuLen1 = strlen(m_Num1.pXiaoshu);
int nXiaoshuLen2 = strlen(m_Num2.pXiaoshu);
if (nXiaoshuLen1 < nXiaoshuLen2)
{
char *pAlignStr = NULL;
if (0 != XiaoshuAlign(cVar1,nLen1 + nXiaoshuLen2 - nXiaoshuLen1,&pAlignStr))
return -1;
PosDecrease(pAlignStr,cVar2,&pResult,bIsNeg);
}
else
{
char *pAlignStr = NULL;
if (0 != XiaoshuAlign(cVar2,nLen2 + nXiaoshuLen1 - nXiaoshuLen2,&pAlignStr))
return -1;
PosDecrease(cVar1,pAlignStr,&pResult,bIsNeg);
}
if (NULL == pResult)
return -1;
*ppResult = pResult;
if (0 != strcmp("",m_Num1.pZhengshu))
{
free(m_Num1.pZhengshu);
m_Num1.pZhengshu = NULL;
}
if (0 != strcmp("",m_Num1.pXiaoshu))
{
free(m_Num1.pXiaoshu);
m_Num1.pZhengshu = NULL;
}
if (0 != strcmp("",m_Num2.pZhengshu))
{
free(m_Num2.pZhengshu);
m_Num2.pZhengshu = NULL;
}
if (0 != strcmp("",m_Num2.pXiaoshu))
{
free(m_Num2.pXiaoshu);
m_Num2.pXiaoshu = NULL;
}
return 0;

}


整体思路和上面是对应的,具体实现可能小的地方没有完善好。在提交、修改七八次后,终于全部通过了所有用例。

尾记:第一遍提交的时候,结果为“运行时间超限”,查找之后发现是,strcpy函数用法错误的问题。后来自己测用例的时候出现了CRT detected that the application wrote to memory after end of heap buffer的问题,调试后定位到错误的原因是调用free函数时出错,找找后发现还是分配内存后,使用strcpy的没注意到字符串长度问题,解决的办法是用strncpy,比较安全。


                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: