I’ve been doing the Weekly
Challenges. The
latest
involved string construction and numerical sequence analysis. (Note
that this ends today.)
Task 1: Special Notes
You are given two strings, $source and $target.
Write a script to find out if using the characters (only once) from source, a target string can be created.
Those who skip a week are apparently condemned to repeat it, since
this is basically 221 part 1 with the order of the parameters reversed
and only one input string rather than a whole list of them. (It's also
quite like 216 part 2, from which I took word2map
for the languages
I didn't use in 221.1.)
Perl:
sub word2map($word) {
my %w;
map {$w{$_}++} split '',lc($word);
return \%w;
}
sub specialnotes($chars, $word) {
my $cm = word2map($chars);
my $f = word2map($word);
my $valid = 1;
foreach my $c (keys %{$f}) {
if (!exists $cm->{$c} || $f->{$c} > $cm->{$c}) {
$valid = 0;
last;
}
}
return $valid;
}
Task 2: Additive Number
You are given a string containing digits 0-9 only.
Write a script to find out if the given string is additive number.
An additive number is a string whose digits can form an additive
sequence.
A valid additive sequence should contain at least 3 numbers. Except
the first 2 numbers, each subsequent number in the sequence must be
the sum of the preceding two.
e.g. "199100199" can be broken down as "1 + 99 = 100, 99 + 100 = 199".
The best I came up with was a depth-first search scanning across the
sequence. The three elements in the stack tuple are:
- start index of first value
- end index of first value
- end index of second value
because the start of the second value is always one higher than the
end of the first, so I can calculate that on the fly. The stack is
pre-seeded with a start index of zero, and all possible end indices,
for the first pair of numbers.
For each pass through the stack, we check all possibilities for the
end index of the third value, which starts immediately after the
second. If the value defined by those indices matches the sum of the
values from the first two, that's a valid continuation of the
sequence - so what goes back on the stack is the indices for the
second and third values, and we'll continue next pass. (Unless we've
got to the end of the sequence, in which case, return true
and all
is well.)
An optimisation I didn't bother with would be to reduce the scanning
space: two values of A
and B
digits cannot add to one of more than
max(A, B) + 1
digits. However, since we allow zeroes, they may add
to fewer than max(A, B)
digits.
PostScript, where I used stack marking to employ the actual language
stack as my LIFO stack:
/exdigits {
3 dict begin
/e exch def
/s exch def
/digits exch def
0
s 1 e {
exch 10 mul exch
digits exch get add
} for
end
} bind def
/additivenumber {
1 dict begin
[ exch
s2a {
48 sub
} forall
] /digits exch def
mark
0 1 digits length 3 sub {
/i exch def
i 1 add 1 digits length 2 sub {
/j exch def
[ 0 i j ]
} for
} for
/valid false def
{
counttomark 0 eq {
cleartomark
exit
} if
aload pop
/end_b exch def
/end_a exch def
/start_a exch def
/start_b end_a 1 add def
/val_ab digits start_a end_a exdigits
digits start_b end_b exdigits add def
/start_c end_b 1 add def
start_c 1 digits length 1 sub {
/end_c exch def
val_ab digits start_c end_c exdigits eq {
end_c digits length 1 sub eq {
/valid true def
exit
} {
[ start_b end_b end_c ]
} ifelse
} if
} for
valid {
cleartomark
exit
} if
} loop
valid
end
} bind def
and for the same algorithm in a more conventional language, Python:
def exdigits(digits, start, end):
x = 0
for i in range(start, end + 1):
x *= 10
x += digits[i]
return x
def additivenumber(digitstring):
digits = [int(x) for x in digitstring]
stack = []
for i in range(len(digits) - 2):
for j in range(i + 1, len(digits) - 1):
stack.append((0, i, j))
while len(stack) > 0:
start_a, end_a, end_b = stack.pop()
start_b = end_a + 1
val_ab = exdigits(digits, start_a, end_a) + exdigits(digits, start_b, end_b)
start_c = end_b + 1
for end_c in range(start_c, len(digits)):
if val_ab == exdigits(digits, start_c, end_c):
if end_c == len(digits) - 1:
return True
else:
stack.append((start_b, end_b, end_c))
break
return False
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.