Peter’s blog ✴ Week 398 ✴ 2 November 2026

THE WEEKLY CHALLENGE
Divisible and difference

The Perl Camel

Task 2

String difference

You are given an array of equal length strings. Write a script to find the difference array of each supplied word. There is one word with a difference array which is not the same as any other word’s difference array. Return that word.

The difference array of a word comprises the series of differences between the ordinal values of successive letters. For exampe the difference array of abd is 1, 2, because:

ord('b') - ord('a') == 1; ord('d') - ord('b') == 2.

Examples


Example 1
Input: @words = ('ab', 'bc', 'cd', 'zy')
Output: 'zy'
'ab' => [1 - 0] = [1]
'bc' => [2 - 1] = [1]
'cd' => [3 - 2] = [1]
'zy' => [24 - 25] = [-1]

Example 2
Input: @words = ('cba', 'fed', 'azy')
Output: 'azy'
'cba' => [1 - 2, 0 - 1] = [-1, -1]
'fed' => [4 - 5, 3 - 4] = [-1, -1]
'azy' => [25 - 0, 24 - 25] = [25, -1]

Example 3
Input: @words = ('pop', 'ada', 'beb', 'cfc')
Output: 'pop'
'pop' => [14 - 15, 15 - 14] = [-1, 1]
'ada' => [3 - 0, 0 - 3] = [3, -3]
'beb' => [4 - 1, 1 - 4] = [3, -3]
'cfc' => [5 - 2, 2 - 5] = [3, -3]

Example 4
Input: @words = ('aaaa', 'zzzz', 'mmmm', 'aaba')
Output: 'aaba'
'aaaa' => [0 - 0, 0 - 0, 0 - 0] = [0, 0, 0]
'zzzz' => [25 - 25, 25 - 25, 25 - 25] = [0, 0, 0]
'mmmm' => [12 - 12, 12 - 12, 12 - 12] = [0, 0, 0]
'aaba' => [0 - 0, 1 - 0, 0 - 1] = [0, 1, -1]

Example 5
Input: @words = ('aceg', 'bdfh', 'cegi', 'azbz')
Output: 'azbz'
'aceg' => [2 - 0, 4 - 2, 6 - 4] = [2, 2, 2]
'bdfh' => [3 - 1, 5 - 3, 7 - 5] = [2, 2, 2]
'cegi' => [4 - 2, 6 - 4, 8 - 6] = [2, 2, 2]
'azbz' => [25 - 0, 1 - 25, 25 - 1] = [25, -24, 24]

Analysis

Once I got my head around the requirement - which I have slightly restated above - this wasn't too hard. I chose to represent the difference arrays as strings, for example acb as 2:-1, rather than arrays, as arrays can be messy to compare.

I note that for this challenge to make sense, the strings must have at least 2 characters, the strings must all have the same number of characters, and there must be at least 3 strings. My solution does not check that these constraints are met, but I would do so in production code.

My algorithm works as follows. First it creates all the difference strings (as above) and builds a hash where the hash index is the difference string and its value is a count of words having that string. So, for example, if 3 strings have the difference string 2:-1 then $count{'2:-1'} == 3.

Then the first word - in any order - with a difference string that occurs just once is the required answer, because by the rules of the challenge there can only be one word whose difference string appears only once.

Perl Weekly’s review

from PW issue 798

The post for the Weekly Challenge presents a brilliant but simple Perl script that does the filtering as well as the mean-rounding in a few lines. It also has a great mechanism for dealing with edge cases when criteria is missing while providing an excellent mobile development experience using terminal as well as text editor.

This review may cover either or both challenges for this week.

Try it 

Your input:



eg: 'aceg', 'bdfh', 'cegi', 'azbz'

Script


#!/usr/bin/perl

# Blog: http://ccgi.campbellsmiths.force9.co.uk/challenge/398/2

use v5.26;    # The Weekly Challenge - 2026-11-02
use utf8;     # Week 398 - task 2 - String difference
use warnings; # Peter Campbell Smith
binmode STDOUT, ':utf8';
use Encode;

string_difference('ab', 'bc', 'cd', 'zy');
string_difference('cba', 'fed', 'azy');
string_difference('pop', 'ada', 'beb', 'cfc');
string_difference('aaaa', 'zzzz', 'mmmm', 'aaba');
string_difference('aceg', 'bdfh', 'cegi', 'azbz');
string_difference('challenge', 'paradigm', 'dibmmfohf', 'fkdoohqjh');
string_difference('spam', 'spam', 'spam');

sub string_difference {
    
    my (@words, $word_length, $j, $k, @diff, %count);
    
    @words = @_;
    say qq[\nInput:  ('] . join(q[', '], @words) . q[')];

    # get difference arrays (as strings)
    $word_length = length($words[0]);
    for $j (0 .. $#words) {
        for $k (0 .. $word_length - 2) {
            $diff[$j] .= ord(substr($words[$j], $k + 1, 1)) -
                ord(substr($words[$j], $k, 1)) . ':';
        }
        $count{$diff[$j]} ++;
    }
    
    # seek odd man out - the difference string which occurs just once
    for $j (0 .. $#words) {
        if ($count{$diff[$j]} == 1) {
            say qq[Output: '$words[$j]'];
            return;
        }
    }
    say qq[Output: input is invalid];
}

14 lines of code

Output from script


Input:  ('ab', 'bc', 'cd', 'zy')
Output: 'zy'

Input:  ('cba', 'fed', 'azy')
Output: 'azy'

Input:  ('pop', 'ada', 'beb', 'cfc')
Output: 'pop'

Input:  ('aaaa', 'zzzz', 'mmmm', 'aaba')
Output: 'aaba'

Input:  ('aceg', 'bdfh', 'cegi', 'azbz')
Output: 'azbz'

Input:  ('challenge', 'paradigm', 'dibmmfohf', 'fkdoohqjh')
Output: 'paradigm'

Input:  ('spam', 'spam', 'spam')
Output: input is invalid

 

Any content of this website which has been created by Peter Campbell Smith is in the public domain