每周一道leetcode—— 43. Multiply Strings

題目:

給定兩個(gè)字符串十進(jìn)制數(shù)字寒砖,給出字符串為他們的乘積佛嬉。要求如下:

  1. 禁止使用內(nèi)置大數(shù)算法。
  2. 字符串長度110
  3. 輸入無前置0
  4. 字符串僅含有數(shù)字

思考

在一開始沒有看到禁止使用內(nèi)置函數(shù),我直接使用python的strint這兩個(gè)函數(shù),結(jié)果直接前10%了秽褒。。威兜。后來我看了下題目原來是不能用內(nèi)置函數(shù)的销斟,不知道這個(gè)python語言的特性算不算內(nèi)置大數(shù)算法。這是一個(gè)大數(shù)乘法問題椒舵,大數(shù)乘法有很多現(xiàn)成的算法蚂踊。

最開始提交的python版本,擊敗88%

class Solution(object):
    def multiply(self, num1, num2):
        return str(int(num1)*int(num2))

最原始的算法就是模擬手算了吧笔宿,首先你得實(shí)現(xiàn)字符串的加法計(jì)算悴势,多位乘法乘一位乘法,這樣應(yīng)該可以計(jì)算多位乘以多位了措伐。例如計(jì)算12345*67890,就是計(jì)算12345*6 + 12345*7 + 12345 *8 + 12345 * 9 + 12345 * 0军俊, 然后每一個(gè)計(jì)算結(jié)果后面補(bǔ)0侥加,再相加就會(huì)得到結(jié)果。不過這樣的速度會(huì)比較慢粪躬。

況且担败,CPU本身就可以計(jì)算一些不會(huì)溢出的加法,所以镰官,我們得好好利用這一點(diǎn)提前。首先我們得改進(jìn)一下上面的算法,計(jì)算12345*67890這個(gè)數(shù)字的結(jié)果泳唠,我們可以這樣算:12345*67890 = (12300 + 45)*(67800 + 90) 狈网,顯然可以拆解成為4個(gè)乘法計(jì)算,而且對于低位為0的,可以直接去掉拓哺,然后計(jì)算結(jié)果再補(bǔ)回來就行了勇垛,如果這四個(gè)乘法分別計(jì)算的時(shí)候不會(huì)溢出,那就沒有問題了士鸥。否則闲孤,我們可以繼續(xù)分解。

使用karatsuba乘法可以繼續(xù)改進(jìn)上面一點(diǎn)烤礁。
我們注意到加法中有12300*90+45*67800讼积,我們可以利用已經(jīng)計(jì)算過的結(jié)果,也就是12300*6780045*90脚仔,然后只需要計(jì)算(12300+45)*(67800+90) - 12300*67800 - 45*90 就可以得到 12300*90 + 45*67800勤众。這樣,乘法的計(jì)算次數(shù)就減少了一次玻侥。

karatsuba乘法寫起來會(huì)復(fù)雜一點(diǎn)决摧,先不實(shí)現(xiàn)。首先提交一版n^2的算法也就是普通版凑兰,看看效果怎么樣掌桩,然后再改進(jìn)。(結(jié)果表明姑食,手算版的速度已經(jīng)很快了)

第一版

首先我們得寫一個(gè)字符串相加的算法波岛。我們首先觀察一下輸入的數(shù)據(jù)類型和輸出的數(shù)據(jù)類型。是string類型的音半,那么按照一位一位加起來也是可以的则拷。可以直接用兩個(gè)整形數(shù)組保存一下每一位數(shù)曹鸠,然后相加算出來第三個(gè)數(shù)組煌茬,這時(shí)第三個(gè)數(shù)組會(huì)有一些數(shù)字超出了10,我們按照從低位開始向高位進(jìn)一位彻桃。最后將這個(gè)數(shù)組轉(zhuǎn)成字符串就可以了坛善。

雖然本地測試還可以,但是提交后速度很慢邻眷,排名很后眠屎,只擊敗10%。

改進(jìn)

我覺得理論上這個(gè)算法應(yīng)該是不慢的,但是實(shí)際過程是很慢肆饶,可能是由于多余的整形和字符串互相轉(zhuǎn)換有關(guān)改衩。上面的算法里面,在計(jì)算乘法的時(shí)候?qū)⒆址D(zhuǎn)成了數(shù)字但是之后又轉(zhuǎn)換回字符串驯镊,可能是這里產(chǎn)生了多余的時(shí)間葫督。所以竭鞍,應(yīng)該直接在這里將結(jié)果加在最終結(jié)果上面。

提交后運(yùn)行時(shí)間為9ms候衍,擊敗50%笼蛛。

改進(jìn)2

算法中相同的字符串重復(fù)轉(zhuǎn)化,可能會(huì)消耗時(shí)間蛉鹿。
對字符串轉(zhuǎn)化進(jìn)行緩存滨砍,第一次轉(zhuǎn)換成功就進(jìn)行緩存,以后如果需要直接取出不需要進(jìn)行額外的計(jì)算妖异。

最終代碼惋戏,可直接編譯運(yùn)行。運(yùn)行時(shí)間6ms他膳,擊敗76%提交响逢。看來字符串轉(zhuǎn)化反而成了性能瓶頸棕孙。

#include <stdio.h>
#include <string>
#include <stdlib.h>
#include <iostream>
#include <vector>


using namespace std;

const int BINARY = 10;

class Solution
{
  private:
    vector<int> result;
    string _num1, _num2;
    long num1buff[120];
    long num2buff[120];

  public:
    string multiply(string num1, string num2)
    {
        result.clear();
        result.resize(num1.length() + num2.length() + 1);
        memset(num1buff, -1 , sizeof(long)*120);
        memset(num2buff, -1 , sizeof(long)*120);
        _num1 = num1;
        _num2 = num2;
        for(auto &c : _num1){
            c-='0';
        }
        for(auto &c : _num2){
            c-='0';
        }
        //這個(gè)過程是遞歸的過程,我們先看下什么時(shí)候終止吧.
        //終止的時(shí)候,是兩個(gè)數(shù)相乘不會(huì)溢出的時(shí)候
        //我們假設(shè)int是二進(jìn)制30位的,相乘要30位,原來兩個(gè)數(shù)字必然是15位的, 也就是32768.
        //也就是說兩個(gè)四位數(shù)相乘通常都不會(huì)溢出了吧.

        //使用遞歸來計(jì)算乘積
        addMultiply(0,num1.length(), 0, num2.length());

        string ret;
        int i = result.size() -1;
        for(; i>0; i--)
        {
            if(result[i] != 0) break;
        }
        for(; i>=0; i--)
        {
            ret.push_back(result[i] + '0');
        }
        return ret;
    }

    void addMultiply(int a1, int a2, int b1, int b2 )
    {;
        //判斷是否可以直接計(jì)算
        if (a1 == a2 || b1 == b2)
            return;
        if (a2 - a1 < 10 && b2 - b1 < 10)
        {
            long int_num1 = getLong1(a1, a2);
            long int_num2 = getLong2(b1, b2);
            long output = int_num1 * int_num2;
            int pos = _num1.length() + _num2.length() - a2 - b2;
            while (output != 0 || result[pos] >= BINARY)
            {
                long a = output % BINARY;
                result[pos] += a;
                result[pos + 1] += result[pos] / BINARY;
                result[pos] %= BINARY;
                output /= BINARY;
                pos++;
            }
            return;
        }
        //否則,拆開較長的那個(gè)數(shù)字
        if(a2 - a1 >= 10){
            addMultiply(a1, (a2 + a1)/2, b1, b2);
            addMultiply((a2 + a1)/2, a2, b1, b2);
        }
        else {
            addMultiply(a1, a2, (b1+b2)/2, b2);
            addMultiply(a1, a2, b1, (b1+b2)/2);
        }
    }
    long getLong1(int a, int b){
        long ret = 0;
        if(num1buff[a] != -1) return num1buff[a];
        for(int i=a; i!=b;i++){
            ret *= BINARY;
            ret += _num1[i] ;
        }
        num1buff[a] = ret;
        return ret;
    }
    long getLong2(int a, int b){
        long ret = 0;
        if(num2buff[a] != -1) return num2buff[a];
        for(int i=a; i!=b;i++){
            ret *= BINARY;
            ret += _num2[i] ;
        }
        num2buff[a] = ret;
        return ret;
    }
};

int main(void)
{
    Solution s;
    for(int i=0;i<10000;i++){
        cout << s.multiply("12345678901", "100") << endl;
        cout << s.multiply("100", "100") << endl;
    }
    return 0;
}

最終排名擊敗70%

排名
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末舔亭,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子蟀俊,更是在濱河造成了極大的恐慌钦铺,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件肢预,死亡現(xiàn)場離奇詭異矛洞,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)烫映,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進(jìn)店門沼本,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人锭沟,你說我怎么就攤上這事抽兆。” “怎么了族淮?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵郊丛,是天一觀的道長。 經(jīng)常有香客問我瞧筛,道長,這世上最難降的妖魔是什么导盅? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任较幌,我火速辦了婚禮,結(jié)果婚禮上白翻,老公的妹妹穿的比我還像新娘乍炉。我一直安慰自己绢片,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布岛琼。 她就那樣靜靜地躺著底循,像睡著了一般。 火紅的嫁衣襯著肌膚如雪槐瑞。 梳的紋絲不亂的頭發(fā)上熙涤,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天,我揣著相機(jī)與錄音困檩,去河邊找鬼祠挫。 笑死,一個(gè)胖子當(dāng)著我的面吹牛悼沿,可吹牛的內(nèi)容都是我干的等舔。 我是一名探鬼主播,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼糟趾,長吁一口氣:“原來是場噩夢啊……” “哼慌植!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起义郑,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤蝶柿,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后魔慷,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體只锭,經(jīng)...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年院尔,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蜻展。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,981評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡邀摆,死狀恐怖纵顾,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情栋盹,我是刑警寧澤施逾,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布,位于F島的核電站例获,受9級特大地震影響汉额,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜榨汤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一蠕搜、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧收壕,春花似錦妓灌、人聲如沸轨蛤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽祥山。三九已至,卻和暖如春掉伏,著一層夾襖步出監(jiān)牢的瞬間缝呕,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工岖免, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留岳颇,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓颅湘,卻偏偏與公主長得像话侧,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子闯参,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,933評論 2 355

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理瞻鹏,服務(wù)發(fā)現(xiàn),斷路器鹿寨,智...
    卡卡羅2017閱讀 134,657評論 18 139
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法新博,類相關(guān)的語法,內(nèi)部類的語法脚草,繼承相關(guān)的語法赫悄,異常的語法,線程的語...
    子非魚_t_閱讀 31,632評論 18 399
  • 首頁 資訊 文章 資源 小組 相親 登錄 注冊 首頁 最新文章 IT 職場 前端 后端 移動(dòng)端 數(shù)據(jù)庫 運(yùn)維 其他...
    Helen_Cat閱讀 3,874評論 1 10
  • 《裕語言》速成開發(fā)手冊3.0 官方用戶交流:iApp開發(fā)交流(1) 239547050iApp開發(fā)交流(2) 10...
    葉染柒丶閱讀 26,776評論 5 19
  • 現(xiàn)在各類學(xué)習(xí)類的網(wǎng)站和APP十分多馏慨,令人應(yīng)接不暇埂淮。尤其隨著自媒體的興起,知識(shí)越來越被重視写隶,知識(shí)變現(xiàn)也成了目前最熱的...
    楊梅泡酒閱讀 238評論 0 1