问题
I have a string of numerical values separated with spaces:
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...
I want to get all occurrences of the string, where combined sum of numbers is x. So, if I search: 2041 it should return an array ("318 500 358 865") or if I search: 1818 it should return an array ("920 898", "31 470 725 358 114 56 9 55").
I also need solution to be optimal, because string of numbers can sum up to hundreds of thousands.
Principle explained in theory would be good, but preferred languages are PHP or NodeJs if solution is given by programming language. Even MySQL solution would be nice, if possible. But solution by any language would be nice anyway.
Extra notes
- numbers are all positive integers
- number values are between 1-10000
回答1:
Here you go, in Python. This has linear complexity:
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) )
I'm assuming there are no negative values in your list, and the list consists purely of integers separated by spaces. You shouldn't have any problems translating it into any other language. I hope the algorithm is self-explanatory, if not ask.
回答2:
Usage:
$ex = new finder($string);
$result = $ex->search(979)->find(true); // true = Hard mode
First we pass the string we want to search, then we use method to_array() so we can create the array from the string, then you need to choose which mode you want to use.
It has 2 modes, soft search and hard search.
you can choose which one to use in the find method, ->find(true) , True = hard mode, False = soft mode;
The soft mode it is really simple, creates a new multidimensional array with keys as the strlen of the numbers (method order()), so if we are searching for a number 70 we don't really wanna use the 3 digits or more, i mean ( 100, 99999 etc... ). Then it just minus the search value with the number and tries to search in the entire array with (in_array) if it has the result;
foreach ($this->_clean as $clean) {
$minus = abs($clean - $search);
// Simple and fast query
if(in_array($minus, $this->_clean)){
$tmp[] = array($minus,$clean);
}
}
The hard mode is just as the name states, we will loop through every number.
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;
}
Of course we could really make a few more tweaks but from what i tested it gives good results. Feel free to ask if you have any questions.
Example with outputs: 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);
Output example for 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
)
)
来源:https://stackoverflow.com/questions/21380268/matching-the-sum-of-values-on-string