动态规划(最长公共子序列LCS)

概念

  • 求解决策过程最优化的结果 (可能有多个)
  • 把多阶段过程转化为一系列单阶段过程,利用各阶段之间的关系,逐个求解
  • 计算过程中会把结果都记录下,最终结果在记录中找到.

举例

  • 求两个字符串的最长公共子序列
  • 字符串如下
    x : bdcaba 纵坐标
    y : abcbdab 横坐标

矩阵展示

x/yabcbdab
b0101001
d0111222
c0122222
a1122233
b0223334
a1223344

填表公式:

php代码实现

   /**
     * php 实现动态规划 最长公共子序列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    = [];
            //字符串拆分为数组
            $this->xa       = str_split($x);
            $this->ya       = str_split($y);
            //1、生成矩阵
            $this->createMatrix();
            //1、输出矩阵页面到屏幕并且记录最优解的下标
            $this->writeMatrix();
            //3、获取所有可能走向
            foreach($this->indexs as $index){
                $data =  $this->getWay($index['i'],$index['j'],$this->wayCount);
                $this->wayCount++;
            }
            //4、输出结果
            $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]){
                    //移动位置过程 
                    $this->moves[$n][]   = sprintf('%s:%s:%s:true',$this->xa[$xlen],$this->ya[$ylen],$this->matrix[$xlen][$ylen]);
                    //移动点数据
                    $this->results[$n][] = $this->xa[$xlen];
                    $xlen--;
                    $ylen--;
                }
                else{
                    //移动位置过程 
                    $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条路可走需要进行扩散
                    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);
                        //之前的继续走
                        $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];
                    }
                }
            }
        }


        /**
         * 输出矩阵到屏幕方便分析并且记录最优解所在坐标
         * @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) {
                    //取出所有的最优解
                    if($rvv == $this->max){
                        $this->indexs[] = [
                            'i' => $ri,
                            'j' => $rvi,
                        ];
                    }
                    echo $rvv," ";
                }
                echo PHP_EOL;
            }
        }

        /**
         * 输出结果到屏幕方便对比
         * @return 
         */
        private function writeResults(){
            echo '----------移动过程--------------',PHP_EOL;
            echo '格式 纵坐标:横坐标:层级:是否符合',PHP_EOL,PHP_EOL;
            foreach ($this->moves as $move) {
                if(empty($move)){
                    continue;
                }
                //输出从后向前找的过程 纵坐标:横坐标 : 层级
                echo implode(" -> ",$move),PHP_EOL;
            }
            echo '----------最终结果--------',PHP_EOL;
            foreach ($this->results as $result) {
                if(empty($result)){
                    continue;
                }
                //得到的是逆序结果 反转数组后输出
                $data = array_reverse($result);
                echo implode(" ",$data),PHP_EOL;
            }
        }
    }


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

执行结果

    ----------矩阵列表--------------

      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 
    ----------移动过程--------------
    格式 纵坐标:横坐标:层级:是否符合

    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
    ----------最终结果--------
    b d a b
    b c a b
    b c b a
    b d a b
    b c b a
    b c a b

总结分析

  • 第一步主要生成列表
  • 第一步主要获取最优解的最大长度 并且符合长度的下标都取出
  • 第三步 根据所有最优解下标从后向左前方向一步一步走,哪里大走向哪里
  • 重点部分 如果值相等说明出现2种情况 需要扩散第二条路 无限扩散
  • 最终得到所有情况 输出即可
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

sltin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值