Chris@0
|
1 <?php
|
Chris@0
|
2 /*
|
Chris@0
|
3 * This file is part of the Diff package.
|
Chris@0
|
4 *
|
Chris@0
|
5 * (c) Sebastian Bergmann <sebastian@phpunit.de>
|
Chris@0
|
6 *
|
Chris@0
|
7 * For the full copyright and license information, please view the LICENSE
|
Chris@0
|
8 * file that was distributed with this source code.
|
Chris@0
|
9 */
|
Chris@0
|
10
|
Chris@0
|
11 namespace SebastianBergmann\Diff\LCS;
|
Chris@0
|
12
|
Chris@0
|
13 /**
|
Chris@0
|
14 * Memory-efficient implementation of longest common subsequence calculation.
|
Chris@0
|
15 */
|
Chris@0
|
16 class MemoryEfficientImplementation implements LongestCommonSubsequence
|
Chris@0
|
17 {
|
Chris@0
|
18 /**
|
Chris@0
|
19 * Calculates the longest common subsequence of two arrays.
|
Chris@0
|
20 *
|
Chris@0
|
21 * @param array $from
|
Chris@0
|
22 * @param array $to
|
Chris@0
|
23 *
|
Chris@0
|
24 * @return array
|
Chris@0
|
25 */
|
Chris@0
|
26 public function calculate(array $from, array $to)
|
Chris@0
|
27 {
|
Chris@0
|
28 $cFrom = count($from);
|
Chris@0
|
29 $cTo = count($to);
|
Chris@0
|
30
|
Chris@0
|
31 if ($cFrom == 0) {
|
Chris@0
|
32 return array();
|
Chris@0
|
33 } elseif ($cFrom == 1) {
|
Chris@0
|
34 if (in_array($from[0], $to)) {
|
Chris@0
|
35 return array($from[0]);
|
Chris@0
|
36 } else {
|
Chris@0
|
37 return array();
|
Chris@0
|
38 }
|
Chris@0
|
39 } else {
|
Chris@0
|
40 $i = intval($cFrom / 2);
|
Chris@0
|
41 $fromStart = array_slice($from, 0, $i);
|
Chris@0
|
42 $fromEnd = array_slice($from, $i);
|
Chris@0
|
43 $llB = $this->length($fromStart, $to);
|
Chris@0
|
44 $llE = $this->length(array_reverse($fromEnd), array_reverse($to));
|
Chris@0
|
45 $jMax = 0;
|
Chris@0
|
46 $max = 0;
|
Chris@0
|
47
|
Chris@0
|
48 for ($j = 0; $j <= $cTo; $j++) {
|
Chris@0
|
49 $m = $llB[$j] + $llE[$cTo - $j];
|
Chris@0
|
50
|
Chris@0
|
51 if ($m >= $max) {
|
Chris@0
|
52 $max = $m;
|
Chris@0
|
53 $jMax = $j;
|
Chris@0
|
54 }
|
Chris@0
|
55 }
|
Chris@0
|
56
|
Chris@0
|
57 $toStart = array_slice($to, 0, $jMax);
|
Chris@0
|
58 $toEnd = array_slice($to, $jMax);
|
Chris@0
|
59
|
Chris@0
|
60 return array_merge(
|
Chris@0
|
61 $this->calculate($fromStart, $toStart),
|
Chris@0
|
62 $this->calculate($fromEnd, $toEnd)
|
Chris@0
|
63 );
|
Chris@0
|
64 }
|
Chris@0
|
65 }
|
Chris@0
|
66
|
Chris@0
|
67 /**
|
Chris@0
|
68 * @param array $from
|
Chris@0
|
69 * @param array $to
|
Chris@0
|
70 *
|
Chris@0
|
71 * @return array
|
Chris@0
|
72 */
|
Chris@0
|
73 private function length(array $from, array $to)
|
Chris@0
|
74 {
|
Chris@0
|
75 $current = array_fill(0, count($to) + 1, 0);
|
Chris@0
|
76 $cFrom = count($from);
|
Chris@0
|
77 $cTo = count($to);
|
Chris@0
|
78
|
Chris@0
|
79 for ($i = 0; $i < $cFrom; $i++) {
|
Chris@0
|
80 $prev = $current;
|
Chris@0
|
81
|
Chris@0
|
82 for ($j = 0; $j < $cTo; $j++) {
|
Chris@0
|
83 if ($from[$i] == $to[$j]) {
|
Chris@0
|
84 $current[$j + 1] = $prev[$j] + 1;
|
Chris@0
|
85 } else {
|
Chris@0
|
86 $current[$j + 1] = max($current[$j], $prev[$j + 1]);
|
Chris@0
|
87 }
|
Chris@0
|
88 }
|
Chris@0
|
89 }
|
Chris@0
|
90
|
Chris@0
|
91 return $current;
|
Chris@0
|
92 }
|
Chris@0
|
93 }
|