On Jun 20, 6:20 am, sharad <sharad20073...@gmail.com> wrote:
> Given a dictionary of words, find out the longest chain of 'ancestors'
> in it. Ancestors are defined in the following way: A word is said to
> be the parent of another word if
>      1) It is a valid word in the dictionary
>      2) It can be obtained by deleting one character of the word and
> permuting the remaining characters.
>      eg. run is a parent of urns
>     A possible chain of ancestors could be:
>     a
>     an
>     ban
>     nabs
>     bands
> This chain is of length 4. What is the length of the longest chain in
> a given dictionary?

I solved this using an array of hash tables, C, where C[n] is for
strings of length n.  The key of each hash table entry is a string
formed by sorting the lowercased characters of the word it
represents.  The corresponding value is a triple of the form [ word,
chain length, subkey ].  The subkey is just the key for the next
shortest word in the chain.  It effectively serves as a pointer so you
can get the final answer.

Process the dictionary in sorted order (ignoring case).  For word W,
compute key(W).  Let W_i be W with the i'th character deleted.  Then
also compute all possible subkeys S_i = key(W_i).  For each subkey
S_i, check the hash table C[length(W) - 1] to see if S_i is there.  If
not and C[length(W)] is not already defined for W, then add [W, 1,
null] to C[length] with key key(W).  If there is already a value for
S_i in the table C[length(W) - 1], say it is [X, L, S], then add [W, L
+ 1, S_i] to C[length(W)] with key key(W) if L+1 is bigger than any
previously stored value.

Note that you could save memory by throwing away all the information
about strings that are more than one letter shorter than the current
W.  But then you would need a real pointer rather than a subkey in the
hash table in order to retrieve the final answer.

I implemented all the above in perl.  Below is the result using the
dictionary on my Macbook.  It takes about 7 seconds to run including
reading the dictionary.

Read 234936 words.
Polystomatidae
Platystomidae
Platysomidae
plasmodiate
metapodial
diplomate
diplomat
diploma
amidol
amido
amid
aim
ai
A

Here is the code.  It was very quickly written. No claim that it's
correct.

sub find_chains {
    my $dict = shift;
    my @chains;
    my $longest;
    foreach my $word (@$dict) {
        my $n = length($word);
        my $key = join '', sort split '', lc($word);
        foreach my $i (0 .. length($key) - 1) {
            my $subkey = substr($key, 0, $i) . substr($key, $i + 1);
            my $chain = $n > 1 && $chains[$n - 1] && $chains[$n - 1]-
>{$subkey};
            my $new_chain;
            my $existing = $chains[$n]->{$key};
            if ($chain) {
                if (!$existing || $existing->[1] < $chain->[1] + 1) {
                    $new_chain = $chains[$n]->{$key} = [$word, $chain->[1] + 1,
$subkey];
                }
            }
            else {
                if (!$existing) {
                    $new_chain = $chains[$n]->{$key} = [$word, 1, ''];
                }
            }
            if ($new_chain && (!$longest || $new_chain->[1] > $longest->[1]))
{
                $longest = $new_chain;
            }
        }
    }
    my $p = $longest;
    while ($p) {
        print "$p->[0]\n";
        $p = $chains[$p->[1] - 1]->{$p->[2]};
    }
}

sub main {
    open F, "/usr/share/dict/words" or die $!;
    my @words = map { chomp; $_ } <F>;
    close F;
    print "Read " . scalar(@words) . " words.\n";
    find_chains(\...@words);
}

main;

-- 
You received this message because you are subscribed to the Google Groups 
"Algorithm Geeks" group.
To post to this group, send email to algoge...@googlegroups.com.
To unsubscribe from this group, send email to 
algogeeks+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/algogeeks?hl=en.

Reply via email to