RogerBW's Blog

The Weekly Challenge 224: Specially Additive Number Notes 09 July 2023

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.

See also:
The Weekly Challenge 216: Word Registration
The Weekly Challenge 221: Good Arithmetic

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.

Search
Archive
Tags 1920s 1930s 1940s 1950s 1960s 1970s 1980s 1990s 2000s 2010s 3d printing action advent of code aeronautics aikakirja anecdote animation anime army astronomy audio audio tech base commerce battletech beer boardgaming book of the week bookmonth chain of command children chris chronicle church of no redeeming virtues cold war comedy computing contemporary cornish smuggler cosmic encounter coup covid-19 crime crystal cthulhu eternal cycling dead of winter doctor who documentary drama driving drone ecchi economics en garde espionage essen 2015 essen 2016 essen 2017 essen 2018 essen 2019 essen 2022 essen 2023 existential risk falklands war fandom fanfic fantasy feminism film firefly first world war flash point flight simulation food garmin drive gazebo genesys geocaching geodata gin gkp gurps gurps 101 gus harpoon historical history horror hugo 2014 hugo 2015 hugo 2016 hugo 2017 hugo 2018 hugo 2019 hugo 2020 hugo 2021 hugo 2022 hugo 2023 hugo 2024 hugo-nebula reread in brief avoid instrumented life javascript julian simpson julie enfield kickstarter kotlin learn to play leaving earth linux liquor lovecraftiana lua mecha men with beards mpd museum music mystery naval noir non-fiction one for the brow opera parody paul temple perl perl weekly challenge photography podcast politics postscript powers prediction privacy project woolsack pyracantha python quantum rail raku ranting raspberry pi reading reading boardgames social real life restaurant reviews romance rpg a day rpgs ruby rust scala science fiction scythe second world war security shipwreck simutrans smartphone south atlantic war squaddies stationery steampunk stuarts suburbia superheroes suspense television the resistance the weekly challenge thirsty meeples thriller tin soldier torg toys trailers travel type 26 type 31 type 45 vietnam war war wargaming weather wives and sweethearts writing about writing x-wing young adult
Special All book reviews, All film reviews
Produced by aikakirja v0.1