I’ve been doing the Perl Weekly
Challenges. The
latest
involved string isomorphism and interval merging. (Note that this is
open until 27 December 2020.)
TASK #1 › Isomorphic Strings
You are given two strings $A
and $B
.
Write a script to check if the given strings are Isomorphic. Print 1
if they are otherwise 0.
sub isos {
my @s=@_;
Well, if they aren't the same length they certainly aren't isomorphic.
if (length($s[0]) != length($s[1])) {
return 0;
}
For each string I have an array of its characters, a mapping table,
and a mapping index.
my @c=map {[split '',$_]} @s;
my @lt=({},{});
my @n=(0,0);
Then for each character index in the strings:
foreach my $ci (0..$#{$c[0]}) {
my @r;
For each string, map the character to an integer, either one I've seen
before in this string or a newly-allocated one. (Yeah, it uses more
storage this way than the standard solution, which is to map
characters in a to characters in b, but it felt cleaner and more
symmetrical.)
foreach my $si (0,1) {
if (exists $lt[$si]{$c[$si][$ci]}) {
push @r,$lt[$si]{$c[$si][$ci]};
} else {
$lt[$si]{$c[$si][$ci]}=$n[$si];
push @r,$n[$si];
$n[$si]++;
}
}
If the two characters don't map to the same integer, the comparison
has failed.
if ($r[0] != $r[1]) {
return 0;
}
}
return 1;
}
Raku and Ruby are similar but for syntax. In Python a string is
already an array of characters, and in Rust it can be made into one.
fn isos(a: String,b: String) -> i64 {
if &a.len() != &b.len() {
return 0;
}
let s: Vec<Vec<char>>=vec![a.chars().collect(),b.chars().collect()];
let mut lt: std::vec::Vec<std::collections::HashMap<char, i64>> = vec![HashMap::new(),HashMap::new()];
let mut n=vec![0,0];
for ci in 0..s[0].len() {
let mut r: std::vec::Vec<i64>=vec![];
for si in 0 as usize..=1 {
if lt[si].contains_key(&s[si][ci]) {
r.push(*lt[si].get(&s[si][ci]).unwrap());
} else {
lt[si].insert(s[si][ci],n[si]);
r.push(n[si]);
n[si]+=1;
}
}
if r[0] != r[1] {
return 0;
}
}
return 1;
}
TASK #2 › Insert Interval
You are given a set of sorted non-overlapping intervals and a new interval.
Write a script to merge the new interval to the given set of intervals.
One of those standard compsci problems which I don't think I've ever
had to do in real life. Anyway, the key distractor here is the word
"sorted"; I'll just sort the new interval into the existing list and
then do a standard interval collapse on it (which will also work if
they aren't sorted or aren't non-overlapping).
sub ii {
my ($iv,$nv)=@_;
my @q=@{$iv};
push @q,$nv;
@q=sort {$a->[0] <=> $b->[0]} @q;
@q
is now a list of all intervals sorted by starting point.
my @out;
foreach my $il (@q) {
For each new element, if the output list is empty or the top value in
the output list is smaller than the bottom value of the new interval,
just append new interval to output.
if (scalar @out == 0 ||
$out[-1][1] < $il->[0]) {
push @out,$il;
} else {
Otherwise, i.e. the top value of the output list overlaps with the
bottom of the new interval, replace that top value with the top of the
new interval.
$out[-1][1]=max($out[-1][1],$il->[1]);
}
}
return \@out;
}
Raku, Python and Ruby go similarly, and even Rust doesn't need a lot
of verbiage:
fn ii(iv: Vec<Vec<i64>>,nv: Vec<i64>) -> Vec<Vec<i64>> {
let mut q=iv;
q.push(nv);
q.sort_by(|a,b| a[0].cmp(&b[0]));
let mut out: Vec<Vec<i64>>=vec![];
for il in q {
let oi=out.len();
if oi == 0 ||
out[oi-1][1] < il[0] {
out.push(il);
} else {
out[oi-1][1]=cmp::max(out[oi-1][1],il[1]);
}
}
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.