There are two strategies for approaching this task. First, we could start working from the words that we receive from the user. For each of these words, we will check if the word is in the dictionary. If it not, we will generate different variants of the word by inserting characters, deleting characters and switching character pairs. If any of these variants is in the dictionary, then we print it. If no known alternative is found, we print "???". The second processing strategy starts from the words dictionary. For every dictionary word we generate variants by inserting characters, deleting characters and switching character pairs. We store all variants in a hash with links to the original forms. When we process incoming text, we can lookup variants in the hash and extract the original forms from the hash. Most processing for the second strategy can be done before dealing with the input of the user. So strategy 2 is faster than strategy 1 but it requires more memory. We will use combination of these two methods. At the top of the program, we define the dictionary as a list of words (@dict). Then we build two data structures: a hash of the words in the dictionary (%dict) and a hash of variants of the dictionary words (%var). These can be created by going through the dictionary list @dict in a loop. Store each word in %dict as key with value 1. Then (still inside the loop) create variants of the word. The first variant type is a word with one character less (deletion). Covert the word to a list of characters (split on //) and start a new loop over all characters (loop variable $i). Remove the $i-th character of the list with splice() and convert the remaining list back to a string (join on ""). Then store the pair of the word and the variant in the variant hash %var with the variant as key and the word as value. Then restore the original list of characters by once more applying split on the word. Repeat this for all characters in the word and for all words in the dictionary. The second variant involves switching two adjacent characters (transposition). Convert the word to a character list and run another loop over all characters. In the loop, swap the current character with the next one. Convert the resulting charcter list back to a string (join with "") and store the resulting variant in the variant hash %var with the variant as key and the original word as value. Restore the original list of characters by once more applying split on the word. Repeat this for all characters in the word but the last one, and for all words in the dictionary. When all dictionary words are processed, read a line of text from the keyboard, perform some cleaning up (delete [^\w\s]+) and convert the resulting string to a list of words (split on /\s+/). Next, process the words one by one. If a word is in the dictionary (%dict) then print it. Otherwise, if it is in the variant hash (%var) then print the original version which can be obtained from the hash. If there is more than one candidate original version, randomly choose one of the alternatives. The variant hash does not cover one of the required errors: character insertion. It is easier to identify these by processing the incoming words. If the word is neither in the dictionary %dict nor in the variant hash %var then we should check if it contains an additional character. Convert the incoming word to a character list (split on //) and process all the words in the list in the same way as in the first dictionary processing method: run a loop over the characters in the word (loop variable $i), remove the $i-th character from the list, convert the list back to a string (join with "") and check if the string is a dictionary word (%dict). If this is the case then print the variant. Otherwise rebuild the original character list from the word (split on //) and process the next word. When deleting characters does not produce a usable variant then print the default: "???". Process all the words from the input in this way: lookup in %dict first, then in %var if necessary and finally delete characters from them. This sequence of code should be put in a separate subroutine with one argument (word). The loop over the words can be put in the main program.