给出范围列表,即:1-3,5,6-4,31,9,19,10,25-20
如何将其减少到1-6,9-10,19-25,31
?
这是我到目前为止所做的,看起来有点复杂,所以 是否有任何更简单/更聪明的方法来做到这一点。
$in = '1-3,5,6-4,31,9,19,10,25-20';
// Explode the list in ranges
$rs = explode(',', $in);
$tmp = array();
// for each range of the list
foreach($rs as $r) {
// find the start and end date of the range
if (preg_match('/(\d+)-(\d+)/', $r, $m)) {
$start = $m[1];
$end = $m[2];
} else {
// If only one date
$start = $end = $r;
}
// flag each date in an array
foreach(range($start,$end) as $i) {
$tmp[$i] = 1;
}
}
$str = '';
$prev = 999;
// for each date of a month (1-31)
for($i=1; $i<32; $i++) {
// is this date flaged ?
if (isset($tmp[$i])) {
// is output string empty ?
if ($str == '') {
$str = $i;
} else {
// if the previous date is less than the current minus 1
if ($i-1 > $prev) {
// build the new range
$str .= '-'.$prev.','.$i;
}
}
$prev = $i;
}
}
// build the last range
if ($i-1 > $prev) {
$str .= '-'.$prev;
}
echo "str=$str\n";
注意:它必须在php 5.1.6下运行(我无法升级)。
仅供参考:数字代表一个月中的日期,因此它们仅限于1-31。
从给定的日期(1-3,6,7-8)
开始,我想获得另一个列表(1-3,6-8)
,其中重新计算并排序所有范围。
答案 0 :(得分:1)
您只需搜索数据即可获得所需内容。拆分分隔符上的输入,在您的情况下','。然后以某种方式对它进行排序,这是您从当前位置左侧搜索的保险箱。拿你的第一个元素,检查它是否是一个范围,并使用此范围内的最大数字(1-3个范围中的3个或3个,如果3是单个元素)进行进一步比较。然后取出列表中的第二个元素,检查它是否是最后一个元素的直接后继元素。如果是,则将第一个和第二个元素/范围组合到一个新范围。重复。
编辑:我不确定PHP,但正则表达式对于这个问题有点矫枉过正。只需在爆炸阵列中查找“ - ”,然后就知道它是一个范围。对exp进行排序数组保证你回溯,你用$ prev做的东西。您还可以在' - '上展开爆炸数组中的每个元素,并检查结果数组的大小是否为&gt; 1来了解元素是否是范围。
答案 1 :(得分:1)
也许不是最有效的,但对于您正在使用的有限范围的值,不应该太糟糕:
$in = '1-3,5,6-4,31,9,19,10,25-20';
$inSets = explode(',',$in);
$outSets = array();
foreach($inSets as $inSet) {
list($start,$end) = explode('-',$inSet.'-'.$inSet);
$outSets = array_merge($outSets,range($start,$end));
}
$outSets = array_unique($outSets);
sort($outSets);
$newSets = array();
$start = $outSets[0];
$end = -1;
foreach($outSets as $outSet) {
if ($outSet == $end+1) {
$end = $outSet;
} else {
if ($start == $end) {
$newSets[] = $start;
} elseif($end > 0) {
$newSets[] = $start.'-'.$end;
}
$start = $end = $outSet;
}
}
if ($start == $end) {
$newSets[] = $start;
} else {
$newSets[] = $start.'-'.$end;
}
var_dump($newSets);
echo '<br />';
答案 2 :(得分:1)
从算法的角度来看问题,让我们考虑一下你对这个问题的限制。所有号码都是1-31。该列表是“范围”的集合,每个范围由两个数字(开始和结束)定义。没有关于开始是否会更多,更少或等于结束的规则。
由于我们有一个任意大的范围列表,但有一个明确的排序/组织方法,分而治之的策略可能会产生最大的复杂性。
首先,我对如何创建此算法中的每个步骤(分割部分,征服药水,优化等)进行了非常详尽的解释,但解释却非常长。为了缩短它,这是最终答案:
<?php
$ranges = "1-3,5,6-4,31,9,19,10,25-20";
$range_array = explode(',', $ranges);
$include = array();
foreach($range_array as $range){
list($start, $end) = explode('-', $range.'-'.$range); //"1-3-1-3" or "5-5"
$include = array_merge($include, range($start, $end));
}
$include = array_unique($include);
sort($include);
$new_ranges = array();
$start = $include[0];
$count = $start;
// And begin the simple conquer algorithm
for( $i = 1; $i < count($include); $i++ ){
if( $include[$i] != ($count++) ){
if($start == $count-1){
$new_ranges[] = $start;
} else {
$new_ranges[] = $start."-".$count-1;
}
$start = $include[$i];
$count = $start;
}
}
$new_ranges = implode(',', $new_ranges);
?>
这应该(理论上)对任何正整数的任意长度的数组起作用。由于-
是我们的范围分隔符,负整数会被绊倒。