Project Euler in PL/SQL: Problem 17

  • by

Switching back to PL/SQL for Problem 17 of Project Euler.

Problem 17:

Number Letter Counts

If the numbers 1 to 5 are written out in words: one, two, three, four, five, then there are 3 + 3 + 5 + 4 + 4 = 19 letters used in total.

If all the numbers from 1 to 1000 (one thousand) inclusive were written out in words, how many letters would be used?

NOTE: Do not count spaces or hyphens. For example, 342 (three hundred and forty-two) contains 23 letters and 115 (one hundred and fifteen) contains 20 letters. The use of “and” when writing out numbers is in compliance with British usage.

This problem required quit a bit more code than what we’ve done so far. I had to manually handle for each case of numbers, 1-19, and each interval of 10 (20, 30, 40, etc.) since they don’t follow a reusable rule. The cool thing is once those were taken care of, I could handle all numbers up to 9999.

I use one function for all numbers 1-19, and a main function that processes all numbers and calls the handle_LT_twenty function when required.

DECLARE
total INTEGER := 0;
FUNCTION handle_LT_twenty(x INTEGER) RETURN INTEGER --handle "less than" twenty
IS
char_count INTEGER;
BEGIN
    IF x IN (1,2,6,10)
        THEN
            char_count := 3;
        ELSIF x IN (4,5,9)
        THEN
            char_count := 4;
        ELSIF x IN (3,7,8)
        THEN
            char_count := 5;
        ELSIF x IN (11,12)
        THEN
            char_count := 6;
        ELSIF x IN (15,16)
        THEN
            char_count := 7;
        ELSIF x IN (13,14,18,19)
        THEN
            char_count := 8;
        ELSIF x IN (17)
        THEN
            char_count := 9;
    END IF;
    
    return char_count;
END;
FUNCTION count_chars(x INTEGER) RETURN INTEGER
IS
char_count INTEGER := 0;
ones_digit INTEGER := SUBSTR(x,-1,1); -- right-most digit
tens_digit INTEGER := SUBSTR(x,-2,1);
hundreds_digit INTEGER := SUBSTR(x,-3,1);
thousands_digit INTEGER := SUBSTR(x,-4,1);
BEGIN

    --handle for cases of 19 and less
    IF tens_digit IN (1) OR length(x) < 2
    THEN
        char_count := handle_LT_twenty(tens_digit||ones_digit);
    ELSE
    --handle the tens digit for 20 and higher
        IF tens_digit IN (2,3,8,9) THEN -- "twenty", "thirty", etc.
           char_count := 6;
        ELSIF tens_digit IN (4,5,6) THEN -- "forty", "fifty", etc.
            char_count := 5;
        ELSIF tens_digit IN (7) THEN -- "seventy"
            char_count := 7;
        END IF;
        
        IF ones_digit != 0
        THEN
            char_count := char_count + handle_LT_twenty(ones_digit);
        END IF;
        
    END IF;

    IF length(x) >= 3 AND hundreds_digit != 0 THEN --dealing with hundreds
        char_count := char_count + handle_LT_twenty(hundreds_digit); --hundred digit follows same rules as single digit, "one" hundred
        
        IF tens_digit+ones_digit = 0
        THEN
            char_count := char_count + length('hundred'); -- 100 = "onehundred"
        ELSE
            
            char_count := char_count + length('hundredand'); --no spaces! 101 = "onehundredandone"
        END IF;
        
    ELSIF length(x) = 4 THEN --dealing with thousands
        char_count := char_count + handle_LT_twenty(thousands_digit); --thousands digit follows same rules as single digit, "one" thousand
        
        IF hundreds_digit+tens_digit+ones_digit = 0
        THEN
            char_count := char_count + length('thousand'); -- 1000 = "onethousand"
        ELSE
            char_count := char_count + length('thousandand'); --no spaces! 1001 = "onethousandandone"
        END IF;
    END IF;

    return char_count;
END;
BEGIN
    FOR i IN 1..1000
    LOOP
        total := total + count_chars(i);
    END LOOP;
    
    dbms_output.put_line(total);
END;


--output: 21124

Questions or comments? Feel free to leave them below or reach out to me on Twitter!

Leave a Reply

Your email address will not be published.