華爲OJ之正數相減

題目描述:

請編寫程序實現:
兩個任意長度的正數相減,這兩個正數可以帶小數點,也可以是整數,請輸出結果.
系統假設:
輸入的字符串中,不會出現除了數字與小數點以外的其它字符,不會出現多個小數點以及小數點在第一個字符的位置
等非法情況,所以考生的程序中無須考慮輸入的數值字符串非法的情況。
詳細要求以及系統約束
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.輸出的字符串內存由考生的程序負責分配,測試程序負責釋放

本題出自高級篇,該題看起來意思簡單,實則要注意的地方有很多,特別是標紅的地方。簡要說說該題的幾點注意事項:

  1. 由於長度任意,因此想把字符串轉換爲數字後再計算的想法就被槍斃了;
  2. 涉及到小數運算,對齊借位的問題更加麻煩;
  3. 得到結果字符串中“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,比較安全。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章