Skip to content
This repository was archived by the owner on Jul 8, 2023. It is now read-only.

Commit 3d8ff57

Browse files
committed
Implemented PHP-LCS.
1 parent ff2b012 commit 3d8ff57

File tree

12 files changed

+447
-3
lines changed

12 files changed

+447
-3
lines changed

.gitattributes

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
.gitattributes export-ignore
2+
.gitignore export-ignore
3+
.travis.yml export-ignore
4+
phpunit.xml export-ignore
5+
phpunit.reports.xml export-ignore
6+
test export-ignore

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
test/report
2+
vendor

.travis.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
language: php
2+
before_script:
3+
- composer install --dev
4+
script: phpunit
5+
php:
6+
- 5.3.3
7+
- 5.3
8+
- 5.4

LICENSE

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Copyright © 2012 Erin Millard
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy
4+
of this software and associated documentation files (the "Software"), to deal
5+
in the Software without restriction, including without limitation the rights
6+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
copies of the Software, and to permit persons to whom the Software is
8+
furnished to do so, subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in
11+
all copies or substantial portions of the Software.
12+
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
THE SOFTWARE.

README.md

Lines changed: 73 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,74 @@
1-
php-lcs
2-
=======
1+
# PHP-LCS
32

4-
Longest common subsequence implementation in PHP
3+
*An implementation of the 'longest common subsequence' algorithm for PHP.*
4+
5+
## What is PHP-LCS?
6+
7+
PHP-LCS is a PHP implementation of an algorithm to solve the 'longest common
8+
subsequence' problem.
9+
10+
From [Wikipedia](http://en.wikipedia.org/wiki/Longest_common_subsequence_problem):
11+
12+
> The **longest common subsequence (LCS) problem** is to find the longest
13+
> [subsequence](http://en.wikipedia.org/wiki/Subsequence) common to all
14+
> sequences in a set of sequences (often just two). Note that subsequence is
15+
> different from a substring, see
16+
> [substring vs. subsequence](http://en.wikipedia.org/wiki/Subsequence#Substring_vs._subsequence).
17+
> It is a classic [computer science](http://en.wikipedia.org/wiki/Computer_science)
18+
> problem, the basis of [file comparison](http://en.wikipedia.org/wiki/File_comparison)
19+
> programs such as [diff](http://en.wikipedia.org/wiki/Diff), and has
20+
> applications in [bioinformatics](http://en.wikipedia.org/wiki/Bioinformatics).
21+
22+
## Usage
23+
24+
```php
25+
<?php
26+
use Ezzatron\LCS\LCSSolver;
27+
28+
$solver = new LCSSolver;
29+
30+
$left = array(
31+
'B',
32+
'A',
33+
'N',
34+
'A',
35+
'N',
36+
'A',
37+
);
38+
$right = array(
39+
'A',
40+
'T',
41+
'A',
42+
'N',
43+
'A',
44+
);
45+
$expectedLCS = array(
46+
'A',
47+
'A',
48+
'N',
49+
'A',
50+
);
51+
52+
$LCS = $solver->longestCommonSubsequence(
53+
$left,
54+
$right
55+
);
56+
57+
if ($LCS === $expectedLCS) {
58+
echo 'LCS solver is working.';
59+
} else {
60+
echo 'LCS solver is not working.';
61+
}
62+
// the above outputs 'LCS solver is working.'
63+
```
64+
65+
## Code quality
66+
67+
PHP-LCS strives to attain a high level of quality. A full test suite is
68+
available, and code coverage is closely monitored.
69+
70+
### Latest revision test suite results
71+
[![Build Status](https://secure.travis-ci.org/ezzatron/php-lcs.png)](http://travis-ci.org/ezzatron/php-lcs)
72+
73+
### Latest revision test suite coverage
74+
<http://ci.ezzatron.com/report/php-lcs/coverage/>

composer.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"name": "ezzatron/php-lcs",
3+
"description": "An implementation of the 'longest common subsequence' algorithm for PHP.",
4+
"keywords": ["longest","common","subsequence","sequence","diff","algorithm"],
5+
"homepage": "https://github.com/ezzatron/php-lcs",
6+
"license": "MIT",
7+
"authors": [
8+
{
9+
"name": "Erin Millard",
10+
"email": "[email protected]",
11+
"homepage": "http://ezzatron.com/"
12+
}
13+
],
14+
"require": {
15+
"php": ">=5"
16+
},
17+
"autoload": {
18+
"psr-0": {
19+
"Ezzatron\\LCS": "src"
20+
}
21+
}
22+
}

composer.lock

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

phpunit.reports.xml

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<phpunit
3+
bootstrap="test/bootstrap.php"
4+
strict="true"
5+
mapTestClassNameToCoveredClassName="true"
6+
convertErrorsToExceptions="true"
7+
convertNoticesToExceptions="true"
8+
convertWarningsToExceptions="true"
9+
verbose="true"
10+
colors="true"
11+
>
12+
<testsuites>
13+
<testsuite>
14+
<directory>test/suite</directory>
15+
</testsuite>
16+
</testsuites>
17+
18+
<logging>
19+
<log
20+
type="coverage-clover"
21+
target="test/report/coverage.xml"
22+
/>
23+
24+
<log
25+
type="coverage-html"
26+
target="test/report/coverage"
27+
charset="UTF-8"
28+
yui="true"
29+
highlight="true"
30+
/>
31+
32+
<log
33+
type="json"
34+
target="test/report/logfile.json"
35+
/>
36+
37+
<log
38+
type="tap"
39+
target="test/report/logfile.tap"
40+
/>
41+
42+
<log
43+
type="junit"
44+
target="test/report/logfile.xml"
45+
logIncompleteSkipped="false"
46+
/>
47+
48+
<log
49+
type="testdox-html"
50+
target="test/report/testdox.html"
51+
/>
52+
53+
<log
54+
type="testdox-text"
55+
target="test/report/testdox.txt"
56+
/>
57+
</logging>
58+
59+
<filter>
60+
<whitelist>
61+
<directory>src</directory>
62+
</whitelist>
63+
</filter>
64+
</phpunit>

phpunit.xml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<phpunit
3+
bootstrap="test/bootstrap.php"
4+
strict="true"
5+
mapTestClassNameToCoveredClassName="true"
6+
convertErrorsToExceptions="true"
7+
convertNoticesToExceptions="true"
8+
convertWarningsToExceptions="true"
9+
verbose="true"
10+
colors="true"
11+
>
12+
<testsuites>
13+
<testsuite>
14+
<directory>test/suite</directory>
15+
</testsuite>
16+
</testsuites>
17+
</phpunit>

src/Ezzatron/LCS/LCSSolver.php

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the PHP-LCS package.
5+
*
6+
* Copyright © 2012 Erin Millard
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Ezzatron\LCS;
13+
14+
class LCSSolver
15+
{
16+
/**
17+
* Returns the longest common subsequence of the given arrays.
18+
*
19+
* See http://en.wikipedia.org/wiki/Longest_common_subsequence_problem
20+
*
21+
* @param array $left The first array.
22+
* @param array $right The second array.
23+
* @param array $additional,... Any number of additional arrays.
24+
*
25+
* @return array The longest common subsequence.
26+
*/
27+
public function longestCommonSubsequence(array $left, array $right)
28+
{
29+
if (func_num_args() > 2) {
30+
$arguments = func_get_args();
31+
array_splice(
32+
$arguments,
33+
0,
34+
2,
35+
array(
36+
$this->longestCommonSubsequence($left, $right)
37+
)
38+
);
39+
40+
return call_user_func_array(
41+
array($this, 'longestCommonSubsequence'),
42+
$arguments
43+
);
44+
}
45+
46+
$m = count($left);
47+
$n = count($right);
48+
49+
// $a[$i][$j] = length of LCS of $left[$i..$m] and $right[$j..$n]
50+
$a = array();
51+
52+
// compute length of LCS and all subproblems via dynamic programming
53+
for ($i = $m - 1; $i >= 0; $i--) {
54+
for ($j = $n - 1; $j >= 0; $j--) {
55+
if ($left[$i] === $right[$j]) {
56+
$a[$i][$j] =
57+
(
58+
isset($a[$i + 1][$j + 1]) ?
59+
$a[$i + 1][$j + 1] :
60+
0
61+
) +
62+
1
63+
;
64+
} else {
65+
$a[$i][$j] = max(
66+
(
67+
isset($a[$i + 1][$j]) ?
68+
$a[$i + 1][$j] :
69+
0
70+
),
71+
(
72+
isset($a[$i][$j + 1]) ?
73+
$a[$i][$j + 1] :
74+
0
75+
)
76+
);
77+
}
78+
}
79+
}
80+
81+
// recover LCS itself
82+
$i = 0;
83+
$j = 0;
84+
$lcs = array();
85+
86+
while($i < $m && $j < $n) {
87+
if ($left[$i] === $right[$j]) {
88+
$lcs[] = $left[$i];
89+
90+
$i++;
91+
$j++;
92+
} elseif (
93+
(
94+
isset($a[$i + 1][$j]) ?
95+
$a[$i + 1][$j] :
96+
0
97+
) >=
98+
(
99+
isset($a[$i][$j + 1]) ?
100+
$a[$i][$j + 1] :
101+
0
102+
)
103+
) {
104+
$i++;
105+
} else {
106+
$j++;
107+
}
108+
}
109+
110+
return $lcs;
111+
}
112+
}

0 commit comments

Comments
 (0)