HEAD
bead
beat
boat
boot
FOOT
####
#!/usr/bin/perl
#ver 1.02
use warnings;
use strict;
my $dict = '2of12.txt';
die <
The program finds a way from one word to other, like this:
% transform.pl love shit
love-lose-lost-loot-soot-shot-shit
HELP
my ($left, $right) = @ARGV[0,1];
for ($left, $right) {
$_ = lc;
}
die "the length of given words is not equal!\n" if length($left) != length $right;
open DICT, $dict or die "Cannot open dictionary $dict: $!";
my @words;
while () {
chomp;
push @words, $_ if length == length $left;
}
eval {
my @ways = ([transform($left, $right, \@words)], [reverse transform($right, $left, \@words)]);
if (@{$ways[0]} != @{$ways[1]}) {
printway( @{$ways[0]} > @{$ways[1]} ? $ways[0] : $ways[1] );
}
elsif (grep {$ways[0]->[$_] ne $ways[1]->[$_]} (0..(scalar(@{$ways[0]}) - 1) )) {
printway($ways[0]);
printway($ways[1]);
}
else {printway($ways[0])}
1;
} or print $@;
sub transform {
my $left = shift;
my $right = shift;
my @words = @{+shift};
my (@left, %left, @right, %right); # @left and @right- arrays containing word relation trees: ([foo], [0, foe], [0, fou], [0, 1, fie] ...)
# %left and %right - indices containing word offsets in arrays @left and @right
$left[0] = [$left];
$right[0] = [$right];
$left{$left} = 0;
$right{$right} = 0;
my $leftstart = 0;
my $rightstart = 0;
my @way;
my (%leftstarts, %rightstarts);
SEARCH:
for (;;) {
my @left_ids = $leftstart..$#left; # choose array of indices of new words
$leftstart = $#left;
die "Cannot solve! Bad word '$left' :(\n" if $leftstarts{$leftstart}++ >2; # finish search if the way could not be found
for my $id (@left_ids) { # come through all new words
my @prefix = @{$left[$id]};
my @patterns = wordpattern(pop @prefix); # build patterns to find related words: foo -> (/^.oo$/,/^f.o$/, /^fo.$/)
push @prefix, $id;
foreach my $word (@words) {
next if $left{$word}; # skip words which are already in the tree
if (scalar grep {$word =~ /$_/} @patterns) { # if matched...
push @left, [@prefix, $word];
$left{$word} = $#left; # add new word to array and index
#print join " ", @{$left[-1]}, "\n"; #debugging
if ( defined(my $r_id = $right{$word}) ) { # and check if the word appears in right index. if yes...
my @end = reverse(print_rel($r_id, \@right));
shift @end;
@way = (print_rel($#left, \@left), @end); # build the way between the words
last SEARCH; # and finish the search
}
}
}
}
my @right_ids = $rightstart..$#right; # all the same :) the tree is build from both ends to speed up the process
$rightstart = $#right;
die "Cannot solve! Bad word '$right' :(\n" if $rightstarts{$rightstart}++ > 2;
for my $id (@right_ids) { # build right relational table
my @prefix = @{$right[$id]};
my @patterns = wordpattern(pop @prefix);
push @prefix, $id;
foreach my $word (@words) {
next if $right{$word};
if (scalar grep {$word =~ /$_/} @patterns) {
push @right, [@prefix, $word];
$right{$word} = $#right;
# print join " ", @{$right[-1]}, "\n"; #debugging
if ( defined(my $l_id = $left{$word}) ) {
my @end = reverse print_rel($#right, \@right);
shift @end;
@way = (print_rel($l_id, \@left), @end);
last SEARCH;
}
}
}
}
}
return @way;
}
sub wordpattern {
my $word = shift;
my @patterns;
for my $i (0..(length($word)-1)) {
substr((my $pat = $word), $i, 1, '.');
push @patterns, qr/^$pat$/;
}
return @patterns;
}
sub print_rel {
my $id = shift;
my $ary = shift;
my @line;
my @rel = @{$ary->[$id]};
push @line, (pop @rel);
foreach my $ref_id (reverse @rel) {
unshift @line, $ary->[$ref_id]->[-1];
}
return wantarray ? @line : join "\n", @line, "";
}
sub printway {
my @way = @{+shift};
print join "-", @way;
print "\n";
}
##
##
#!/usr/bin/perl
#ver 2.00
use warnings;
use strict;
use Storable;
use Text::LevenshteinXS 'distance';
my $dict = '2of12.txt';
die <
The program finds a way from one word to other, like this:
% transform.pl love shit
love-lose-lost-loot-soot-shot-shit
HELP
my ($left, $right) = @ARGV[0,1];
for ($left, $right) {
$_ = lc;
}
die "the length of given words is not equal!\n" if length($left) != length $right;
my $db = -e 'dictionary.db' ? retrieve('dictionary.db') : build_db();
my $len = length $left;
foreach my $word ($left, $right) {
if (!$db->{$len}{$word}) {
foreach my $test (keys %{$db->{$len}}) {
if (distance($word, $test) == 1) {
push @{$db->{$len}{$word}}, $test;
push @{$db->{$len}{$test}}, $word;
}
}
}
}
my $list = $db->{length($left)};
eval {
printway([transform($left, $right, $list)]);
1;
} or print $@;
sub transform {
my $left = shift;
my $right = shift;
my $list = shift;
my (@left, %left, @right, %right); # @left and @right- arrays containing word relation trees: ([foo], [0, foe], [0, fou], [0, 1, fie] ...)
# %left and %right - indices containing word offsets in arrays @left and @right
$left[0] = [$left];
$right[0] = [$right];
$left{$left} = 0;
$right{$right} = 0;
my $leftstart = 0;
my $rightstart = 0;
my @way;
my (%leftstarts, %rightstarts);
SEARCH:
for (;;) {
my @left_ids = $leftstart..$#left; # choose array of indices of new words
$leftstart = $#left;
die "Cannot solve! Bad word '$left' :(\n" if $leftstarts{$leftstart}++ >2; # finish search if the way could not be found
for my $id (@left_ids) { # come through all new words
my @prefix = @{$left[$id]};
my $searched = pop @prefix;
push @prefix, $id;
foreach my $word (@{$list->{$searched}}) {
next if $left{$word}; # skip words which are already in the tree
push @left, [@prefix, $word];
$left{$word} = $#left; # add new word to array and index
#print join " ", @{$left[-1]}, "\n"; #debugging
if ( defined(my $r_id = $right{$word}) ) { # and check if the word appears in right index. if yes...
my @end = reverse(print_rel($r_id, \@right));
shift @end;
@way = (print_rel($#left, \@left), @end); # build the way between the words
last SEARCH; # and finish the search
}
}
}
my @right_ids = $rightstart..$#right; # all the same :) the tree is build from both ends to speed up the process
$rightstart = $#right;
die "Cannot solve! Bad word '$right' :(\n" if $rightstarts{$rightstart}++ > 2;
for my $id (@right_ids) { # build right relational table
my @prefix = @{$right[$id]};
my $searched = pop @prefix;
push @prefix, $id;
foreach my $word (@{$list->{$searched}}) {
next if $right{$word};
push @right, [@prefix, $word];
$right{$word} = $#right;
# print join " ", @{$right[-1]}, "\n"; #debugging
if ( defined(my $l_id = $left{$word}) ) {
my @end = reverse print_rel($#right, \@right);
shift @end;
@way = (print_rel($l_id, \@left), @end);
last SEARCH;
}
}
}
}
return @way;
}
sub print_rel {
my $id = shift;
my $ary = shift;
my @line;
my @rel = @{$ary->[$id]};
push @line, (pop @rel);
foreach my $ref_id (reverse @rel) {
unshift @line, $ary->[$ref_id]->[-1];
}
return wantarray ? @line : join "\n", @line, "";
}
sub printway {
my @way = @{+shift};
print join "-", @way;
print "\n";
}
sub build_db { #thanks to Limbic~Region, http://perlmonks.org/index.pl?node_id=180961
open (my $dict, '<', '2of12.txt') or die "Unable to open '2of12.txt' for reading: $!";
my ($db, %data);
while (<$dict>) {
chomp;
push @{$data{length()}}, $_;
}
for my $len (keys %data) {
my $end = $#{$data{$len}};
for my $i (0 .. $end - 1) {
my $word = $data{$len}[$i];
for my $j ($i + 1 .. $end) {
my $test = $data{$len}[$j];
if (distance($word, $test) == 1) {
push @{$db->{$len}{$word}}, $test;
push @{$db->{$len}{$test}}, $word;
}
}
}
}
store $db, 'dictionary.db';
return retrieve('dictionary.db');
}