Two optimisation assignments at HackerRank that don’t have a correct Perl solution besides mine.
Given: a tree with nodes 1 .. n
, where 1 ≤ n
≤ 105
.
A pair of nodes (a, b)
is a similar
pair if both of the following conditions are true:
a
is the ancestor of node b
abs(a - b) < k
Number of nodes, difference
List of edges: parent child
Number of similar pairs.
#!/usr/bin/perl use warnings; use strict; use feature qw{ say }; *ARGV = *DATA{IO} unless @ARGV || ! -t; my $diff = (split ' ', <>)[1]; my %tree; while (<>) { my ($parent, $child) = split; $tree{$child} = $parent; } my $number_of_similar_pairs; for my $node (keys %tree) { my $parent = $tree{$node}; my @ancestors = $parent; while ($tree{$parent}) { $parent = $tree{$parent}; push @ancestors, $parent; } my @similar = grep abs($_ - $node) <= $diff, @ancestors; $number_of_similar_pairs += @similar; } say $number_of_similar_pairs; __DATA__ 5 2 3 2 3 1 1 4 1 5
#! /bin/bash (time ./similar-pair-naive.pl similar-pair.in0) 2>&1
248 real 0m3.156s user 0m3.142s sys 0m0.004s
Too large to display the whole graph (17MB PNG)
perl -d:NYTProf similar-pair-naive.pl similar-pair.in0 nytprofhtml
node | ancestor mask | count |
---|---|---|
3 | 00000 | 0 |
node | ancestor mask | count |
---|---|---|
3 | 00000 | 0 |
00100 |
node | ancestor mask | count |
---|---|---|
3 | 00000 | 0 |
2 | 00100 | 1 |
node | ancestor mask | count |
---|---|---|
3 | 00000 | 0 |
2 | 00100 | 1 |
1 | 00100 | 2 |
node | ancestor mask | count |
---|---|---|
3 | 00000 | 0 |
2 | 00100 | 1 |
1 | 00100 | 2 |
10100 | 2 |
node | ancestor mask | count |
---|---|---|
3 | 00000 | 0 |
2 | 00100 | 1 |
1 | 00100 | 2 |
4 | 10100 | 3 |
node | ancestor mask | count |
---|---|---|
3 | 00000 | 0 |
2 | 00100 | 1 |
1 | 00100 | 2 |
4 | 10100 | 3 |
5 | 10100 | 4 |
node | ancestor mask | count |
---|---|---|
3 | 00000 | 0 |
2 | 00100 | 1 |
1 | 00100 | 2 |
4 | 10100 | 3 |
5 | 10100 | 4 |
00100 | 4 |
node | ancestor mask | count |
---|---|---|
3 | 00000 | 0 |
2 | 00100 | 1 |
1 | 00100 | 2 |
4 | 10100 | 3 |
5 | 10100 | 4 |
00000 | 4 |
node | ancestor mask | count |
---|---|---|
3 | 00000 | 0 |
2 | 00100 | 1 |
1 | 00100 | 2 |
4 | 10100 | 3 |
5 | 10100 | 4 |
Final result | 4 |
#! /usr/bin/perl # use warnings; use strict; use List::Util qw{ first }; use integer; my ($nodes, $diff) = split ' ', <>; my @children = map [], 0 .. $nodes; my @parent; for (<>) { my ($parent, $son) = split; push @{ $children[$parent] }, 0 + $son; $parent[$son] = 0 + $parent; } my $node = first { ! $parent[$_] } 1 .. $nodes; my $count = 0; my $anc_mask = "\0" x $nodes; my $LENGTH = $diff * 2 + 1; while ($node) { my $from = $node - $diff - 1; my $length = $LENGTH; if ($node <= $diff) { $from = 0; $length = $node + $LENGTH - $diff - 1; } $count += (substr $anc_mask, $from, $length) =~ tr/\1//; if (@{ $children[$node] }) { substr $anc_mask, $node - 1, 1, "\1"; $node = shift @{ $children[$node] }; } else { while (my $old_node = $node) { last if $node = shift @{ $children[ $parent[$node] ] }; substr $anc_mask, ($node = $parent[$old_node]) - 1, 1, "\0"; } } } print $count, "\n";
248 real 0m3.156s user 0m3.142s sys 0m0.004s
248 real 0m0.147s user 0m0.146s sys 0m0.000s
Similar to the Travelling Salesman Problem
But
Output: The minimal time
Answer: 30
Shopper 1: | 1 → 2 → 4 → 5 | 30 |
Shopper 2: | 1 → 3 → 5 | 20 |
Numbers of shops, roads, goods
Goods sold at particular shops
Roads connecting the shops and their length
5 5 5 1 1 1 2 1 3 1 4 1 5 1 2 10 1 3 10 2 4 10 3 5 10 4 5 10
Time: 0
Future: 572, 579, 671, 927
Time: 572
Future: 579, 671, 792
Time: 579
Future: 671, 792
Time: 671
Future: 792
Time: 792
I will, in fact, claim that the difference between a bad programmer and a good one is whether he considers his code or his data structures more important. Bad programmers worry about the code. Good programmers worry about data structures and their relationships.Linus Torvalds
$shgti{$shop}{$goods_set} = $time;
✓ | Test Case #0 | ✓ | Test Case #1 | ✓ | Test Case #2 |
✓ | Test Case #3 | ✓ | Test Case #4 | ✓ | Test Case #5 |
✓ | Test Case #6 | ✓ | Test Case #7 | ✓ | Test Case #8 |
✓ | Test Case #9 | ✓ | Test Case #10 | ✓ | Test Case #11 |
✓ | Test Case #12 | ✓ | Test Case #13 | ⌚ | Test Case #14 |
⌚ | Test Case #15 | ✓ | Test Case #16 | ⌚ | Test Case #17 |
⌚ | Test Case #18 | ✓ | Test Case #19 | ✓ | Test Case #20 |
⌚ | Test Case #21 | ⌚ | Test Case #22 | ⌚ | Test Case #23 |
⌚ | Test Case #24 | ⌚ | Test Case #25 | ⌚ | Test Case #26 |
⌚ | Test Case #27 | ⌚ | Test Case #28 | ⌚ | Test Case #29 |
⌚ | Test Case #30 |
✓ | Test Case #0 | ✓ | Test Case #1 | ✓ | Test Case #2 |
✓ | Test Case #3 | ✓ | Test Case #4 | ✓ | Test Case #5 |
✓ | Test Case #6 | ✓ | Test Case #7 | ✓ | Test Case #8 |
✓ | Test Case #9 | ✓ | Test Case #10 | ✓ | Test Case #11 |
✓ | Test Case #12 | ✓ | Test Case #13 | ⌚ | Test Case #14 |
✓ | Test Case #15 | ⌚ | Test Case #16 | ✓ | Test Case #17 |
✓ | Test Case #18 | ✓ | Test Case #19 | ✓ | Test Case #20 |
✓ | Test Case #21 | ✓ | Test Case #22 | ✓ | Test Case #23 |
✓ | Test Case #24 | ⌚ | Test Case #25 | ⌚ | Test Case #26 |
✓ | Test Case #27 | ⌚ | Test Case #28 | ✓ | Test Case #29 |
✓ | Test Case #30 |
If a programmer designs a program, only half the job is done if they have only designed the data structures.Theng, Jones, and Thimbleby
$tishg{$time}{$shop}{$goods_set}
#!/usr/bin/perl #use warnings; use strict; use integer; use List::Util qw{ sum }; my ($shop_count, $road_count, $goods_count) = split ' ', <>; my $ALL_GOODS = 2 ** (1 + $goods_count) - 2; my %shop_sells; for my $shop (1 .. $shop_count) { my (undef, @goods) = split ' ', <>; $shop_sells{$shop} = sum(0, map 2 ** $_, @goods); } my %road; for (1 .. $road_count) { my ($from, $to, $time) = split ' ', <>; $road{$from}{$to} = $road{$to}{$from} = $time; } my %road_fast = map +( $_ => [ keys %{ $road{$_} } ] ), keys %road; my @future = (0); my %tishg = ( 0 => { 1 => { $shop_sells{1} => undef } } ); # Time Shop Goods my %shgti = ( 1 => { $shop_sells{1} => 0 } ); # Shop Goods Time my %final; my ($start, $end, $middle, $newgoods, $newtime); my $best_time; while (1) { my $next = shift @future; last unless defined $next; last if $best_time && $best_time < $next; for my $from (keys %{ $tishg{$next} } ) { for my $goods (keys %{ $tishg{$next}{$from} }) { for my $to (@{ $road_fast{$from} }) { $newgoods = $goods | $shop_sells{$to}; $newtime = $next + $road{$from}{$to}; next unless ($shgti{$to}{$newgoods} // 1e12) > $newtime; $shgti{$to}{$newgoods} = $newtime; undef $tishg{$newtime}{$to}{$newgoods}; if (! @future) { @future = $newtime; } else { $start = 0; $end = $#future; while ($start < $end) { $middle = ($start + $end) >> 1; if ($future[$middle] > $newtime) { $end = $middle - 1; } elsif ($future[$middle] < $newtime) { $start = $middle + 1; } else { $start = $end = $middle; } } ++$start if $newtime > $future[$start]; if ($start > $#future || $newtime != $future[$start] ) { splice @future, $start, 0, $newtime; } } if ($to == $shop_count) { push @{ $final{$newtime} }, $newgoods; for my $othertime (keys %final) { for my $othergoods ( @{ $final{$othertime} }) { next unless $ALL_GOODS == ($newgoods | $othergoods); my $maxtime = $newtime > $othertime ? $newtime : $othertime; $best_time = $maxtime if ! defined $best_time || $maxtime < $best_time; } } } } } } } print $best_time, "\n";
#!/usr/bin/perl #use warnings; use strict; use integer; use List::Util qw{ sum }; load_input(); my %road_fast = map +( $_ => [ keys %{ $road{$_} } ] ), keys %road; my @future = (0); my %tishg = ( 0 => { 1 => { $shop_sells{1} => undef } } ); # Time Shop Goods my %shgti = ( 1 => { $shop_sells{1} => 0 } ); # Shop Goods Time my %final; my ($start, $end, $middle, $newgoods, $newtime); my $best_time; while (1) { my $next = shift @future; last unless defined $next; last if $best_time && $best_time < $next; for my $from (keys %{ $tishg{$next} } ) { for my $goods (keys %{ $tishg{$next}{$from} }) { for my $to (@{ $road_fast{$from} }) { $newgoods = $goods | $shop_sells{$to}; $newtime = $next + $road{$from}{$to}; next unless ($shgti{$to}{$newgoods} // 1e12) > $newtime; $shgti{$to}{$newgoods} = $newtime; undef $tishg{$newtime}{$to}{$newgoods}; if (! @future) { @future = $newtime; } else { $start = 0; $end = $#future; while ($start < $end) { $middle = ($start + $end) >> 1; if ($future[$middle] > $newtime) { $end = $middle - 1; } elsif ($future[$middle] < $newtime) { $start = $middle + 1; } else { $start = $end = $middle; } } ++$start if $newtime > $future[$start]; if ($start > $#future || $newtime != $future[$start] ) { splice @future, $start, 0, $newtime; } } if ($to == $shop_count) { push @{ $final{$newtime} }, $newgoods; for my $othertime (keys %final) { for my $othergoods ( @{ $final{$othertime} }) { next unless $ALL_GOODS == ($newgoods | $othergoods); my $maxtime = $newtime > $othertime ? $newtime : $othertime; $best_time = $maxtime if ! defined $best_time || $maxtime < $best_time; } } } } } } } print $best_time, "\n";
#!/usr/bin/perl #use warnings; use strict; use integer; use List::Util qw{ sum }; load_input(); cache_roads(); my @future = (0); my %tishg = ( 0 => { 1 => { $shop_sells{1} => undef } } ); # Time Shop Goods my %shgti = ( 1 => { $shop_sells{1} => 0 } ); # Shop Goods Time my %final; my ($start, $end, $middle, $newgoods, $newtime); my $best_time; while (1) { my $next = shift @future; last unless defined $next; last if $best_time && $best_time < $next; for my $from (keys %{ $tishg{$next} } ) { for my $goods (keys %{ $tishg{$next}{$from} }) { for my $to (@{ $road_fast{$from} }) { $newgoods = $goods | $shop_sells{$to}; $newtime = $next + $road{$from}{$to}; next unless ($shgti{$to}{$newgoods} // 1e12) > $newtime; $shgti{$to}{$newgoods} = $newtime; undef $tishg{$newtime}{$to}{$newgoods}; if (! @future) { @future = $newtime; } else { $start = 0; $end = $#future; while ($start < $end) { $middle = ($start + $end) >> 1; if ($future[$middle] > $newtime) { $end = $middle - 1; } elsif ($future[$middle] < $newtime) { $start = $middle + 1; } else { $start = $end = $middle; } } ++$start if $newtime > $future[$start]; if ($start > $#future || $newtime != $future[$start] ) { splice @future, $start, 0, $newtime; } } if ($to == $shop_count) { push @{ $final{$newtime} }, $newgoods; for my $othertime (keys %final) { for my $othergoods ( @{ $final{$othertime} }) { next unless $ALL_GOODS == ($newgoods | $othergoods); my $maxtime = $newtime > $othertime ? $newtime : $othertime; $best_time = $maxtime if ! defined $best_time || $maxtime < $best_time; } } } } } } } print $best_time, "\n";
#!/usr/bin/perl #use warnings; use strict; use integer; use List::Util qw{ sum }; load_input(); cache_roads(); init(); while (1) { my $next = shift @future; last unless defined $next; last if $best_time && $best_time < $next; for my $from (keys %{ $tishg{$next} } ) { for my $goods (keys %{ $tishg{$next}{$from} }) { for my $to (@{ $road_fast{$from} }) { $newgoods = $goods | $shop_sells{$to}; $newtime = $next + $road{$from}{$to}; next unless ($shgti{$to}{$newgoods} // 1e12) > $newtime; $shgti{$to}{$newgoods} = $newtime; undef $tishg{$newtime}{$to}{$newgoods}; if (! @future) { @future = $newtime; } else { $start = 0; $end = $#future; while ($start < $end) { $middle = ($start + $end) >> 1; if ($future[$middle] > $newtime) { $end = $middle - 1; } elsif ($future[$middle] < $newtime) { $start = $middle + 1; } else { $start = $end = $middle; } } ++$start if $newtime > $future[$start]; if ($start > $#future || $newtime != $future[$start] ) { splice @future, $start, 0, $newtime; } } if ($to == $shop_count) { push @{ $final{$newtime} }, $newgoods; for my $othertime (keys %final) { for my $othergoods ( @{ $final{$othertime} }) { next unless $ALL_GOODS == ($newgoods | $othergoods); my $maxtime = $newtime > $othertime ? $newtime : $othertime; $best_time = $maxtime if ! defined $best_time || $maxtime < $best_time; } } } } } } } print $best_time, "\n";
#!/usr/bin/perl #use warnings; use strict; use integer; use List::Util qw{ sum }; load_input(); cache_roads(); init(); while (1) { my $next = shift @future; last unless defined $next; last if $best_time && $best_time < $next; for my $from (keys %{ $tishg{$next} } ) { for my $goods (keys %{ $tishg{$next}{$from} }) { for my $to (@{ $road_fast{$from} }) { try_new_combination(); if (! @future) { @future = $newtime; } else { $start = 0; $end = $#future; while ($start < $end) { $middle = ($start + $end) >> 1; if ($future[$middle] > $newtime) { $end = $middle - 1; } elsif ($future[$middle] < $newtime) { $start = $middle + 1; } else { $start = $end = $middle; } } ++$start if $newtime > $future[$start]; if ($start > $#future || $newtime != $future[$start] ) { splice @future, $start, 0, $newtime; } } if ($to == $shop_count) { push @{ $final{$newtime} }, $newgoods; for my $othertime (keys %final) { for my $othergoods ( @{ $final{$othertime} }) { next unless $ALL_GOODS == ($newgoods | $othergoods); my $maxtime = $newtime > $othertime ? $newtime : $othertime; $best_time = $maxtime if ! defined $best_time || $maxtime < $best_time; } } } } } } } print $best_time, "\n";
#!/usr/bin/perl #use warnings; use strict; use integer; use List::Util qw{ sum }; load_input(); cache_roads(); init(); while (1) { my $next = shift @future; last unless defined $next; last if $best_time && $best_time < $next; for my $from (keys %{ $tishg{$next} } ) { for my $goods (keys %{ $tishg{$next}{$from} }) { for my $to (@{ $road_fast{$from} }) { try_new_combination(); add_sorted($newtime, \@future); if ($to == $shop_count) { push @{ $final{$newtime} }, $newgoods; for my $othertime (keys %final) { for my $othergoods ( @{ $final{$othertime} }) { next unless $ALL_GOODS == ($newgoods | $othergoods); my $maxtime = $newtime > $othertime ? $newtime : $othertime; $best_time = $maxtime if ! defined $best_time || $maxtime < $best_time; } } } } } } } print $best_time, "\n";
#!/usr/bin/perl #use warnings; use strict; use integer; use List::Util qw{ sum }; load_input(); cache_roads(); init(); while (1) { my $next = shift @future; last unless defined $next; last if $best_time && $best_time < $next; for my $from (keys %{ $tishg{$next} } ) { for my $goods (keys %{ $tishg{$next}{$from} }) { for my $to (@{ $road_fast{$from} }) { try_new_combination(); add_sorted($newtime, \@future); if ($to == $shop_count) { update_newtime(); } } } } } print $best_time, "\n";
✓ | Test Case #0 | ✓ | Test Case #1 | ✓ | Test Case #2 |
✓ | Test Case #3 | ✓ | Test Case #4 | ✓ | Test Case #5 |
✓ | Test Case #6 | ✓ | Test Case #7 | ✓ | Test Case #8 |
✓ | Test Case #9 | ✓ | Test Case #10 | ✓ | Test Case #11 |
✓ | Test Case #12 | ✓ | Test Case #13 | ✓ | Test Case #14 |
✓ | Test Case #15 | ✓ | Test Case #16 | ✓ | Test Case #17 |
✓ | Test Case #18 | ✓ | Test Case #19 | ✓ | Test Case #20 |
✓ | Test Case #21 | ✓ | Test Case #22 | ✓ | Test Case #23 |
✓ | Test Case #24 | ✓ | Test Case #25 | ✓ | Test Case #26 |
✓ | Test Case #27 | ✓ | Test Case #28 | ✓ | Test Case #29 |
✓ | Test Case #30 |
... $Test_String = <STDIN> ; if($Test_String =~ /$Regex_Pattern/){ print "true"; } else { print "false"; }
You can only edit the ...
part.
1110110001 => 00111, 10101 7 21 3 * 7 = 21, accept!
use bigint; sub check { my ($bin, $three) = ('0b') x 2; my $s = $_; while ($s) { $three .= chop $s; $bin .= chop $s; } return oct $three == 3 * oct $bin } $Regex_Pattern = qr/(?{ check() })/;
But (?{...})
always matches!
We can use the
(?(condition)yes-pattern|no-pattern)
$Regex_Pattern = qr/^(?(?{ check() }) ^ | (*FAIL) )/x;
$Regex_Pattern = '^(?(?{c()})^|(*F))'
In binary, multiplying by 3 is like shift + add.
TPCiA | 2017 |