【问题标题】:Matching the sum of values on string匹配字符串上的值的总和
【发布时间】:2014-01-27 11:57:42
【问题描述】:

我有一串用空格分隔的数值:

70 58 81 909 70 215 70 1022 580 930 898 70 276 31 11 **920 898** 1503 195 770 573 508 1015 31 8 815 1478 31 1022 31 1506 31 **318 500 358 865** 358 991 518 58 450 420 487 31 1478 108 70 1022 31 215 318 500 61 31 655 1061 918 54 898 31 8 1011 9 8 459 346 770 751 31 346 770 880 1171 688 1680 31 1002 769 500 61 8 702 898 8 1206 31 709 565 8 138 58 215 81 1171 31 288 500 380 70 284 1500 565 31 601 55 1501 490 565 530 56 990 380 1061 770 345 1171 31 55 1100 605 1471 1234 **31 470 725 358 114 56 9 55** 1100 1610 1471 1000 971 565 55 1100 1610 1061 770 345 949 31 370 52 688 1680 770 880 1171 163 249 151 489 653 56 990 380 503 490 770 1376 1056 31 8 64 490 565 55 108 56 1178 501 653 898 860 565 31 315 61 509 108 501 653 31 349 249 151 489 246 56 990 380 1070 573 1663 725 821 31 70 373 1171 490 565 55 108...

我想获取所有出现的字符串,其中数字的总和为 x。因此,如果我搜索:2041 它应该返回一个数组(“318 500 358 865”),或者如果我搜索:1818 它应该返回一个数组(“920 898”、“31 470 725 358 114 56 9 55”)。

我还需要最优解,因为一串数字的总和可以达到数十万。

理论上解释原理会很好,但如果由编程语言给出解决方案,首选语言是 PHP 或 NodeJs。如果可能的话,即使是 MySQL 解决方案也会很好。但无论如何,任何语言的解决方案都会很好。

补充说明

  • 数字都是正整数
  • 数值在 1-10000 之间

【问题讨论】:

  • 任何编程语言,还是您希望手动完成?
  • 所以这是一个关于排列和组合(嗯,特别是组合)的问题?
  • 是的,很好,添加了标签。

标签: php algorithm node.js combinations permutation


【解决方案1】:

用法:

$ex = new finder($string);
$result = $ex->search(979)->find(true); // true = Hard mode

首先我们传递我们要搜索的字符串,然后我们使用方法 to_array() 以便我们可以从字符串创建数组,然后您需要选择要使用的模式。

它有两种模式,软搜索和硬搜索。

在find方法中可以选择使用哪一种,-​​>find(true),True=硬模式,False=软模式;

软模式非常简单,创建一个新的多维数组,其中键作为数字的 strlen(方法 order()),所以如果我们要搜索数字 70,我们真的不想使用 3 位数字或更多,我的意思是(100、99999 等...)。然后它只是用数字减去搜索值,如果有结果,则尝试用 (in_array) 在整个数组中搜索;

foreach ($this->_clean as $clean) {
            $minus = abs($clean - $search);

            // Simple and fast query
            if(in_array($minus, $this->_clean)){
               $tmp[] = array($minus,$clean);
            }

 }

困难模式就像名字所说的那样,我们将遍历每个数字。

 public function hard_search($array, $partial){

       $s = 0;

       foreach ($partial as $x){
           $s += $x;
       }

       if ($s == $this->_search){
            $this->_tmp[] = $partial;
       }

       if ($s < $this->_search){
           for($i=0;$i<count($array);$i++) {
                $remaining = array();
                $n = $array[$i];

                for ($j=$i+1; $j<count($array); $j++) {
                    array_push($remaining, $array[$j]);
                };

                $partial_rec = $partial;
                array_push($partial_rec, $n);

                $this->hard_search($remaining,$partial_rec);
          }
       }

        return $this->_tmp;
    }

当然,我们真的可以再做一些调整,但从我测试的结果来看,它给出了很好的结果。 如果您有任何问题,请随时提出。

输出示例:http://codepad.viper-7.com/INvSNo

class finder {

   public $stop;

   protected $_string,
             $_search,
             $_tmp,
             $_array = array(),
             $_array_order = array(),
             $_clean = array();

   public function __construct($string){
       $this->_string = $string;
   }

   /**
    * String to array
    * 
    * @return \find
    */
   public function to_array(){
       $this->_array = array_keys(
          array_flip(
               explode(' ', $this->_string)
             )
         );

       return $this;
   }

   /**
    * what we are searching for
    * 
    * @param string/int $search
    * @return \finder
    */
   public function search($search){
       $this->reset();
       $this->_search = $search;
       $this->to_array();
       return $this;
   }


    /**
    * 
    * @param type $a // array
    * @param type $total // total
    * @return array
    */
   public function find($hard = false){ 

        $this->_hard = $hard;

        if(is_array($this->_search)){
            foreach($this->_search as $search){
               $result[] = $this->search_array($search);
            }
        }else{
            $result = $this->search_array($this->_search);
        }

        return $result;
    }

   /**********************************
    * SOFT SEARCH
    ***********************************/

   /**
    * Multidimensional Array with strlen as the key
    * 
    * @return \find
    */
   public function order(){

        // Order 
        foreach($this->_array as $n){
            $this->_array_order[strlen($n)][] = $n;
        }

        return $this;
   }

   public function clean($search){

       $tmp = array();

        $check_length = function($number) use($search){

            if($number <= $search){
                return $number;
            }
            return false;
        };

        foreach(range(key($this->_array_order), strlen($search)) as $v){

            $tmp = array_map($check_length,array_merge($tmp, $this->_array_order[$v]));  
        }

        $this->_clean = array_filter($tmp);

        sort($this->_clean);

        return $this;
   }

   public function search_array($search) {

        $res = array();

        if($this->_hard == false){
          $this->order();
          $this->clean($search);
          $res = $this->soft_search($search);
        }else{

           $res = $this->hard_search($this->_array, array());
        }

        return $res;
    }

   /**
     * 
     * @return array
     */
    public function soft_search($search){

        $tmp = array();

        foreach ($this->_clean as $clean) {
            $minus = abs($clean - $search);

            // Simple and fast query
            if(in_array($minus, $this->_clean)){
               $tmp[] = array($minus,$clean);
            }

        }
        return $tmp;
    } 

   /**********************************
    * END SOFT SEARCH
    ***********************************/ 

   public function hard_search($array, $partial){

       $s = 0;

       foreach ($partial as $x){
           $s += $x;
       }

       if ($s == $this->_search){
            $this->_tmp[] = $partial;
       }

       // if higher STOP
       if ($s < $this->_search){
           for($i=0;$i<count($array);$i++) {
                $remaining = array();
                $n = $array[$i];

                for ($j=$i+1; $j<count($array); $j++) {
                    array_push($remaining, $array[$j]);
                };

                $partial_rec = $partial;
                array_push($partial_rec, $n);

                $this->hard_search($remaining,$partial_rec);
          }
       }

        return $this->_tmp;
    }



   /****************************
    * 
    * Extra
    * 
    *****************************/ 

   public function reset(){
        $this->_search = '';
        $this->_clean = array();
        $this->_array = array();
        $this->_array_order = array();
        $this->_tmp = array();
        $this->_tmp = array();
        return $this;

   }

   public function new_string($string){

       $this->reset();
       $this->_string = $string;
       $this->to_array();

       return $this;
   }
}

$string = '70 58 81 909 70 215 130 70 1022 580 930 898 70 276 31 11 920 898 1503 195 770 573 508 '
        . '1171 490 565 55 108';

$ex = new finder($string);

echo '<pre>';
echo '<h1>Hard 979</h1>';
$result = $ex->search(979)->find(true);
print_r($result);

echo '<h1>Soft 979</h1>';
$result = $ex->search(979)->find();
print_r($result);

echo '<h1>Hard 238</h1>';
$result = $ex->search(238)->find(true);
print_r($result);

echo '<h1>Soft 238</h1>';
$result = $ex->search(238)->find();
print_r($result);

285 的输出示例:

Hard 285

Array
(
    [0] => Array
        (
            [0] => 70
            [1] => 215
        )

    [1] => Array
        (
            [0] => 58
            [1] => 130
            [2] => 31
            [3] => 11
            [4] => 55
        )

)
Soft 285

Array
(
    [0] => Array
        (
            [0] => 215
            [1] => 70
        )

)

【讨论】:

    【解决方案2】:

    在这里,在 Python 中。这具有线性复杂性:

    def list2string( alist ):
        return " ".join( map(str, alist ))
    
    def string2list( s ):
        return list(map( int, s.split() ))
    
    def find_number( a, total ):
        u = 0
        y = 0 # the current sum of the interval (u .. v)
        res = []
        for v in range( 0, len(a) ):
            # at this point the interval sum y is smaller than the requested total,
            # so we move the right end of the interval forward
            y += a[v]
            while y >= total:
                if y == total:
                    res.append( list2string( a[ u : v+1 ] ) )
                # if the current sum is too large, move the left end of the interval forward
                y -= a[u]
                u += 1
        return res
    
    
    
    text = "70 58 81 909 70 215 70 1022 580 930 898"
    alist = string2list(text)
    print( find_number( alist, 285) )
    

    我假设您的列表中没有负值,并且该列表完全由空格分隔的整数组成。将其翻译成任何其他语言应该没有任何问题。如果不问,我希望算法是不言自明的。

    【讨论】:

    • 不错的一个。所以你从一开始就总结价值。如果总和大于总和,则继续下一个数字。您可以在代码中添加一些 cmets,这会很棒。还有一个需要考虑的额外问题:如果您需要搜索总计列表 [285, 1092] 或将列表作为参数传递并稍微修改函数,您会多次调用相同的函数吗?
    • 好的。反复调用函数,没有简单的方法可以将这个算法推广到多个数字。
    • 如果你尝试使用这个字符串( text = "70 58 81 909 215 1022 580 930 898" )这将不起作用。还是很好的考虑
    • Jorge,我认为它仍然给出正确答案(= 空列表),因为您的字符串上没有与 285 的总和匹配的组合。
    • 这个问题不是标记为php
    猜你喜欢
    • 2020-05-04
    • 1970-01-01
    • 1970-01-01
    • 2020-08-27
    • 1970-01-01
    • 1970-01-01
    • 2015-10-30
    • 1970-01-01
    • 2021-02-10
    相关资源
    最近更新 更多