aboutsummaryrefslogtreecommitdiff
path: root/challenge-096/abigail/perl/ch-2.pl
blob: c84dec7ad1a62973e83352583decdf959ce659bf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#!/opt/perl/bin/perl

#
# See ../README.md
#

#
# Run as: perl ch-2.pl < input-file
#

use 5.032;

use strict;
use warnings;
no  warnings 'syntax';

use experimental 'signatures';
use experimental 'lexical_subs';

use List::Util 'min';

#
# The challenge isn't quite clear on whether we should output a number
# (the minimal number of operations required), or the actual operations.
# The examples show both -- but separated by a blank line. Previous
# challenges typically use a blank line to separate the required output
# from the explaination on why that it is the correct answer.
#
# We're opting to only print the number of operations, not the actual
# operations.
#

#
# This is an implementation of the Wagner Fischer algorithm, which
# calculates the Levenshtein distance.
#
# See https://en.wikipedia.org/wiki/Wagner%E2%80%93Fischer_algorithm
#
sub LevenshteinDistance ($first, $second) {
    my $distance;
    for (my $i = 0; $i <= length ($first); $i ++) {
        for (my $j = 0; $j <= length ($second); $j ++) {
            $$distance [$i] [$j] =
                $i == 0 || $j == 0 ? $i + $j
              : min ($$distance [$i - 1] [$j]     + 1,
                     $$distance [$i]     [$j - 1] + 1,
                     $$distance [$i - 1] [$j - 1] + 
                        (substr ($first,  $i - 1, 1) eq
                         substr ($second, $j - 1, 1) ? 0 : 1))
        }
        #
        # We only need the previous row; this reduces the memory
        # from Theta (N * M) to O (N + M), where N and M are the
        # lengths of the input strings.
        #
        undef $$distance [$i - 1] if $i;
    }
    $$distance [-1] [-1];
}


say LevenshteinDistance /\S+/g for <>;