华为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,比较安全。
相关文章推荐