動(dòng)態(tài)規(guī)劃(最長公共子序列LCS)

概念

  • 求解決策過程最優(yōu)化的結(jié)果 (可能有多個(gè))
  • 把多階段過程轉(zhuǎn)化為一系列單階段過程,利用各階段之間的關(guān)系,逐個(gè)求解
  • 計(jì)算過程中會(huì)把結(jié)果都記錄下,最終結(jié)果在記錄中找到.

舉例

  • 求兩個(gè)字符串的最長公共子序列
  • 字符串如下
    x : bdcaba 縱坐標(biāo)
    y : abcbdab 橫坐標(biāo)

矩陣展示

x/y a b c b d a b
b 0 1 0 1 0 0 1
d 0 1 1 1 2 2 2
c 0 1 2 2 2 2 2
a 1 1 2 2 2 3 3
b 0 2 2 3 3 3 4
a 1 2 2 3 3 4 4

填表公式:


image.png

php代碼實(shí)現(xiàn)

   /**
     * php 實(shí)現(xiàn)動(dòng)態(tài)規(guī)劃 最長公共子序列LCS
     */
    class LcsTest
    {
        private $xa;
        private $ya;
        private $max;
        private $wayCount;
        private $matrix;
        private $indexs;
        private $results;
        private $moves;

        public function lcs($x,$y){
            //屬性重置 
            $this->max      = 0;
            $this->wayCount = 0;
            $this->matrix   = [];
            $this->indexs   = [];
            $this->results  = [];
            $this->moves    = [];
            //字符串拆分為數(shù)組
            $this->xa       = str_split($x);
            $this->ya       = str_split($y);
            //1旗国、生成矩陣
            $this->createMatrix();
            //1鸵膏、輸出矩陣頁面到屏幕并且記錄最優(yōu)解的下標(biāo)
            $this->writeMatrix();
            //3具篇、獲取所有可能走向
            foreach($this->indexs as $index){
                $data =  $this->getWay($index['i'],$index['j'],$this->wayCount);
                $this->wayCount++;
            }
            //4、輸出結(jié)果
            $this->writeResults();
            
        }

        /**
         * 從后向前尋找可能走向
         * @param  integer $xlen 
         * @param  integer $ylen 
         * @param  integer $n    
         * @return 
         */
        private function getWay($xlen,$ylen,$n){
            isset($this->results[$n]) || $this->results[$n] = [];
            isset($this->moves[$n])   || $this->moves[$n]   = [];
            while ($xlen>=0&&$ylen>=0) {
                if($this->xa[$xlen]==$this->ya[$ylen]){
                    //移動(dòng)位置過程 
                    $this->moves[$n][]   = sprintf('%s:%s:%s:true',$this->xa[$xlen],$this->ya[$ylen],$this->matrix[$xlen][$ylen]);
                    //移動(dòng)點(diǎn)數(shù)據(jù)
                    $this->results[$n][] = $this->xa[$xlen];
                    $xlen--;
                    $ylen--;
                }
                else{
                    //移動(dòng)位置過程 
                    $this->moves[$n][]   = sprintf('%s:%s:%s:false',$this->xa[$xlen],$this->ya[$ylen],$this->matrix[$xlen][$ylen]);
                    if($this->matrix[$xlen-1][$ylen]>$this->matrix[$xlen][$ylen-1]){
                        $xlen--;
                    }
                    //如果相等證明2條路可走需要進(jìn)行擴(kuò)散
                    else if($this->matrix[$xlen-1][$ylen]==$this->matrix[$xlen][$ylen-1]){
                        $this->wayCount++;
                        //新開辟一條
                        $this->results[$this->wayCount] = $this->results[$n];
                        $this->getWay($xlen,$ylen-1,$this->wayCount);
                        //之前的繼續(xù)走
                        $this->getWay($xlen-1,$ylen,$n);
                        break;
                    }
                    else{
                        $ylen--;
                    }
                }
            }
        }

        /**
         * 生成矩陣
         * @return 
         */
        private function createMatrix(){
            foreach ($this->xa as $xi => $xv) {
                foreach ($this->ya as $yi => $yv) {
                    if($xv==$yv){
                        $this->matrix[$xi][$yi] = $xi==0||$yi==0?1:$this->matrix[$xi-1][$yi-1]+1;
                    } 
                    else {
                        $this->matrix[$xi][$yi] = $xi==0||$yi==0?0:max([$this->matrix[$xi-1][$yi],$this->matrix[$xi][$yi-1]]);
                    }
                    if($this->matrix[$xi][$yi]>$this->max){
                        $this->max = $this->matrix[$xi][$yi];
                    }
                }
            }
        }


        /**
         * 輸出矩陣到屏幕方便分析并且記錄最優(yōu)解所在坐標(biāo)
         * @return 
         */
        private function writeMatrix(){
            echo '----------矩陣列表--------------',PHP_EOL,PHP_EOL;
            echo " ",implode(" ",$this->ya),PHP_EOL;
            foreach ($this->matrix as $ri => $rv) {
                echo $this->xa[$ri]," ";
                foreach ($rv as $rvi => $rvv) {
                    //取出所有的最優(yōu)解
                    if($rvv == $this->max){
                        $this->indexs[] = [
                            'i' => $ri,
                            'j' => $rvi,
                        ];
                    }
                    echo $rvv," ";
                }
                echo PHP_EOL;
            }
        }

        /**
         * 輸出結(jié)果到屏幕方便對(duì)比
         * @return 
         */
        private function writeResults(){
            echo '----------移動(dòng)過程--------------',PHP_EOL;
            echo '格式 縱坐標(biāo):橫坐標(biāo):層級(jí):是否符合',PHP_EOL,PHP_EOL;
            foreach ($this->moves as $move) {
                if(empty($move)){
                    continue;
                }
                //輸出從后向前找的過程 縱坐標(biāo):橫坐標(biāo) : 層級(jí)
                echo implode(" -> ",$move),PHP_EOL;
            }
            echo '----------最終結(jié)果--------',PHP_EOL;
            foreach ($this->results as $result) {
                if(empty($result)){
                    continue;
                }
                //得到的是逆序結(jié)果 反轉(zhuǎn)數(shù)組后輸出
                $data = array_reverse($result);
                echo implode(" ",$data),PHP_EOL;
            }
        }
    }


    $test = new LcsTest();
    $test->lcs("bdcaba","abcbdab");

執(zhí)行結(jié)果

    ----------矩陣列表--------------

      a b c b d a b
    b 0 1 0 1 0 0 1 
    d 0 1 1 1 2 2 2 
    c 0 1 2 2 2 2 2 
    a 1 1 2 2 2 3 3 
    b 0 2 2 3 3 3 4 
    a 1 2 2 3 3 4 4 
    ----------移動(dòng)過程--------------
    格式 縱坐標(biāo):橫坐標(biāo):層級(jí):是否符合

    b:b:4:true -> a:a:3:true -> c:d:2:false -> d:d:2:true -> b:b:1:true
    c:b:2:false -> c:c:2:true -> d:b:1:false -> b:b:1:true
    a:a:4:true -> b:d:3:false -> b:b:3:true -> a:c:2:false -> c:c:2:true -> d:b:1:false -> b:b:1:true
    a:b:4:false -> b:b:4:true -> a:a:3:true -> c:d:2:false -> d:d:2:true -> b:b:1:true
    a:a:4:true -> b:d:3:false -> b:b:3:true -> a:c:2:false -> c:c:2:true -> d:b:1:false -> b:b:1:true
    c:b:2:false -> c:c:2:true -> d:b:1:false -> b:b:1:true
    ----------最終結(jié)果--------
    b d a b
    b c a b
    b c b a
    b d a b
    b c b a
    b c a b

總結(jié)分析

  • 第一步主要生成列表
  • 第一步主要獲取最優(yōu)解的最大長度 并且符合長度的下標(biāo)都取出
  • 第三步 根據(jù)所有最優(yōu)解下標(biāo)從后向左前方向一步一步走,哪里大走向哪里
  • 重點(diǎn)部分 如果值相等說明出現(xiàn)2種情況 需要擴(kuò)散第二條路 無限擴(kuò)散
  • 最終得到所有情況 輸出即可
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末况凉,一起剝皮案震驚了整個(gè)濱河市菠齿,隨后出現(xiàn)的幾起案子剥槐,更是在濱河造成了極大的恐慌损谦,老刑警劉巖陆馁,帶你破解...
    沈念sama閱讀 221,576評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件找颓,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡叮贩,警方通過查閱死者的電腦和手機(jī)叮雳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門想暗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人帘不,你說我怎么就攤上這事说莫。” “怎么了寞焙?”我有些...
    開封第一講書人閱讀 168,017評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵储狭,是天一觀的道長。 經(jīng)常有香客問我捣郊,道長辽狈,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,626評(píng)論 1 296
  • 正文 為了忘掉前任呛牲,我火速辦了婚禮刮萌,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘娘扩。我一直安慰自己着茸,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,625評(píng)論 6 397
  • 文/花漫 我一把揭開白布琐旁。 她就那樣靜靜地躺著涮阔,像睡著了一般。 火紅的嫁衣襯著肌膚如雪灰殴。 梳的紋絲不亂的頭發(fā)上敬特,一...
    開封第一講書人閱讀 52,255評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音牺陶,去河邊找鬼伟阔。 笑死,一個(gè)胖子當(dāng)著我的面吹牛掰伸,可吹牛的內(nèi)容都是我干的减俏。 我是一名探鬼主播,決...
    沈念sama閱讀 40,825評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼碱工,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼娃承!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起怕篷,我...
    開封第一講書人閱讀 39,729評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤历筝,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后廊谓,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體梳猪,經(jīng)...
    沈念sama閱讀 46,271評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,363評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了春弥。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片呛哟。...
    茶點(diǎn)故事閱讀 40,498評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖匿沛,靈堂內(nèi)的尸體忽然破棺而出扫责,到底是詐尸還是另有隱情,我是刑警寧澤逃呼,帶...
    沈念sama閱讀 36,183評(píng)論 5 350
  • 正文 年R本政府宣布鳖孤,位于F島的核電站,受9級(jí)特大地震影響抡笼,放射性物質(zhì)發(fā)生泄漏苏揣。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,867評(píng)論 3 333
  • 文/蒙蒙 一推姻、第九天 我趴在偏房一處隱蔽的房頂上張望平匈。 院中可真熱鬧,春花似錦藏古、人聲如沸增炭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽弟跑。三九已至灾前,卻和暖如春防症,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背哎甲。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評(píng)論 1 272
  • 我被黑心中介騙來泰國打工蔫敲, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人炭玫。 一個(gè)月前我還...
    沈念sama閱讀 48,906評(píng)論 3 376
  • 正文 我出身青樓奈嘿,卻偏偏與公主長得像,于是被迫代替她去往敵國和親吞加。 傳聞我的和親對(duì)象是個(gè)殘疾皇子裙犹,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,507評(píng)論 2 359

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