Re: [Spoiler] QotW 23

Daniel Martin <martin-+m399P62/[email protected]>
Newsgroups gmane.comp.lang.perl.qotw.discuss
Message-ID <[email protected]>
Rod Adams <[email protected]> writes:

> sub Insert {
>   # Given any previously properly balanced string of parens, including the
>   # empty string, you can insert a '()' at any position you feel like, and
>   # it will remain a properly balanced string.
>   # Unfortunately, I couldn't come up with a way of doing this to avoid
>   # creating the same pattern more than once, so I had to add a %seen hash.
>
>   my ($length) = @_;
>   my %seen = ();
>   _insert('',$length);
>
>   sub _insert {
>     my ($string, $left) = @_;
>     for (my $x = 0 ; $x <= length($string) ; ++$x) {
>       my $newstring = $string;
>       substr($newstring,$x,0,'()');
>       next if $seen{$newstring}++;
>       if ($left == 1) {
>     print "$newstring\n" if EnablePrint;
>       } else {
>     _insert($newstring, $left - 1);
>       }
>     }
>   }
> }

And Bruce J Keeler wrote up essentially the same algorithm in a post
which I haven't gotten through the digest yet.

Anyway, I thought I'd let you know that I've discovered a very simple
way to avoid the necessity of a "seen" hash.  When I remove this from
the program, Bruce Keeler's variation on insert is actually faster
than his second solution (in perl - the C version blows everything
else away in terms of speed) and is even faster than MJD's regexp
monstrosity, which I'm still figuring out.

So here's how to remove the %seen hash; comments continued below:

#!/usr/bin/perl -w

use strict;

my $length = 2 * (shift || 0);

die "Usage: parens <n>\n" unless $length > 0;

my @pipeline = ("()");

while(@pipeline) {
    my $item = shift @pipeline;

    my $limit = index $item, ')';
    for(my $i = 0; $i <= $limit; $i++) {
        my $newitem = $item;
        substr($newitem, $i, 0) = "()";
        if (length $newitem == $length) {
            print "$newitem\n";
        } else {
            push @pipeline, $newitem;
        }
    }
}

__END__

This has the disadvantage of not nicely handling the case when n=0,
but Bruce's program was already die'ing in that case anyway.  I
figured out this by running Bruce's program and then using
Data::Dumper to print out %seen.  Here's what I got - the pattern
should be obvious:

$VAR1 = {
          '(())()()' => 3,
          '()()' => 2,
          '(())' => 1,
          '(()())()' => 3,
          '(())()' => 2,
          '(()()())' => 3,
          '(((())))' => 1,
          '()(()())' => 3,
          '(()())' => 2,
          '()(())()' => 3,
          '(()(()))' => 2,
          '((()))()' => 2,
          '()(())' => 2,
          '()((()))' => 2,
          '()()()' => 3,
          '()()()()' => 4,
          '()' => 1,
          '((())())' => 2,
          '()()(())' => 3,
          '((()()))' => 2,
          '(())(())' => 2,
          '((()))' => 1
        };
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.