All,
A few days ago, I posted a challenge to find a hidden message. The message can be revealed by determining the longest common subsequence of all the lines in the data. I was unable to find an implementation that handled more than two strings so I created my own. I originally imposed several constraints but was hoping someone would provide an efficient (non-brute force) method of handling the general case. The general case being: Here is that code that I ultimately came up with to solve the general case:
#!/usr/bin/perl use strict; use warnings; use Algorithm::Loops 'NestedLoops'; use List::Util 'reduce'; use constant NODE => 0; use constant PATH => 0; use constant CNT => 1; use constant DEPTH => 1; use constant LAST => 2; my @str = map {chomp; $_} <DATA>; print LCS(@str), "\n"; # Longest Common Subsequence in strings sub LCS{ my @str = @_; # Map pos of chars in each str my @pos; for my $i (0 .. $#str) { my $line = $str[$i]; for (0 .. length($line) - 1) { my $char= substr($line, $_, 1); push @{$pos[$i]{$char}}, $_; } } # Find the shortest string my $sh_str = reduce {length($a) < length($b) ? $a : $b} @str; # Coord map & lookup of char across lines # Create permutations if char is duplicate # Skip any where char isn't in every line my (%lookup, @order); CHAR: for my $char (split //, $sh_str) { my @map; for (0 .. $#pos) { next CHAR if ! $pos[$_]{$char}; push @map, $pos[$_]{$char}; } my $next = NestedLoops([@map]); while (my @char_map = $next->()) { my $ref = [@char_map]; $lookup{$ref} = $char; push @order, $ref; } } # Predetermine which char mappings are greater than others my %greater; for my $i (0 .. $#order - 1) { for my $j ($i + 1 .. $#order) { my $gt = is_greater(@order[$i, $j]) or next; my ($lg, $sm) = $gt == 1 ? ($i, $j) : ($j, $i); ++$greater{$order[$sm]}[CNT]; push @{$greater{$order[$sm]}[NODE]}, "$order[$lg]"; } } # A max depth watermark and a path representing that depth my ($max, $path) = (0, ''); # Work queue my @work = map [$_, 1, $_], keys %greater; while (@work) { my $item = pop @work; my ($cur_depth, $route, $last_node) = @{$item}[DEPTH, PATH, LA +ST]; ($max, $path) = ($cur_depth, $route) if $cur_depth > $max; my $left = $greater{$last_node}[CNT]; next if ! $left || $cur_depth + $left <= $max; push @work, map ["$route:$_", $cur_depth + 1, $_], @{$greater{ +$last_node}[NODE]}; } my $hidden_msg = join '', map $lookup{$_}, split /:/, $path; return $hidden_msg; } # All vals in 1 ref > corresponding vals in other ref sub is_greater { my ($ref1, $ref2) = @_; my $cmp = $ref1->[0] <=> $ref2->[0] or return; ($ref1->[$_] <=> $ref2->[$_]) == $cmp || return for 1 .. $#$ref1; return $cmp; }

Do you know of a more efficient way to do this? Are there ways to improve this approach? Do you know how dynamic programming might help (Some implementations for 2 strings I found used DP)? Do you know how this is useful or how can the approach be adapted and applied to be useful?

Cheers - L~R


In reply to Longest Common Subsequence by Limbic~Region

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.