I’ve been doing the Weekly
Challenges. The
latest
involved messing about with lists. (Note that this ends today.)
Task 1: Common Characters
You are given an array of words made up of alphabetic characters
only.
Write a script to return all alphabetic characters that show up in
all words including duplicates.
This fell for me into three major steps. In Perl:
sub commoncharacters ($a) {
Step 1: make a list ac
of hashes of character counts.
my @ac;
foreach my $w (@{$a}) {
my %h;
foreach my $c (split '', $w) {
$h{$c}++;
}
push @ac, \%h;
}
Step 2: make a single hash vc
of minimum character counts (no key if
the character is not present in all words, otherwise the lowest number
of times it occurs).
my %vc = %{$ac[0]};
foreach my $aa (@ac) {
foreach my $c (keys %vc) {
if (exists $aa->{$c}) {
$vc{$c} = min($vc{$c}, $aa->{$c});
} else {
delete $vc{$c};
}
}
}
Step 3: for each character in the (arbitrarily chosen) first input
word, output it if it's present in vc
and decrement its count there,
deleting the key once we've run out of that character to simplify the
testing next time it comes up (e.g. the second "a" in "java").
my @out;
foreach my $c (split '', $a->[0]) {
if (exists $vc{$c}) {
push @out, $c;
$vc{$c}--;
if ($vc{$c} == 0) {
delete $vc{$c};
}
}
}
return \@out;
}
In Rust I could use counter::Counter
which has an overloaded &
to
produce a minimum-count result for step 2, so the whole thing becomes
more compact.
fn commoncharacters(a: Vec<&str>) -> Vec<char> {
let ac =
a.iter().map(|w| w.chars().collect::<Counter<_>>()).collect::<Vec<_>>();
let mut vc = ac[0].clone();
for tc in ac.iter().skip(1) {
vc = vc & tc.clone();
}
let mut out = Vec::new();
for c in a[0].chars() {
if vc.contains_key(&c) {
out.push(c);
vc.subtract(c.to_string().chars());
}
}
out
}
Task 2: Unequal Triplets
You are given an array of positive integers.
Write a script to find the number of triplets (i, j, k)
that
satisfies num[i] != num[j]
, num[j] != num[k]
and num[k] != num[i]
.
I decided to have some fun with this one. The naïve way would be to
generate all valid combinations of (i, j, k)
and check for
inequality. But there's a better way. In Python:
def unequaltriplets(a):
Make a hash of numbers and their counts.
utc = defaultdict(lambda: 0)
for n in a:
utc[n] += 1
Make a list of distinct numbers, and bail out if there will be no triplets.
kl = list(utc.keys())
if len(kl) < 3:
return 0
Start the count.
out = 0
For each triplet of distinct numbers:
for c in combinations(kl, 3):
Increment the count by the product of the number of times each number
occurs, which is the total number of triplets that can be formed. So
if I had an input of [1, 1, 2, 2, 3]
my hash of counts would be {1 => 2, 2 => 2, 3 => 1}
and I'd increment by 2×2×1 = 4
.
out += utc[c[0]] * utc[c[1]] * utc[c[2]]
return out
Full code on
github.
Comments on this post are now closed. If you have particular grounds for adding a late comment, comment on a more recent post quoting the URL of this one.