YHQL YLGL YLFL - A Caesar cipher

In the fourth session (“Classical Ciphers”) of “Daily Study Group: Introduction to Cryptography“ (https://www.bigmarker.com/series/daily-study-group-intro-to-cryptography/series_details ), the following Caesar (shift) cipher was given as a quiz/assignment 

       YHQL YLGL YLFL
         Julius Caesar

I posted some thoughts about this in the Study Group’s Community thread (https://community.wolfram.com/groups/-/m/t/2464878). Since it might be of some interest for  others as well, I’ll create a proper post as well. I also added a simple brute force approach (in “Addendum” below).

First thought

It helped to notice that it was three words of four characters that start with the same character (and a lot of “L”) . It cannot be “alea iacta est” or “aut Caesar aut nihil” for that reason . 

Though I did a brute force to confirm it.

First analysis

Here is my (first) more "formal" analysis of this, assuming that the first word should be in Mathematica' s Latin wordlist .  This mean that we don’t have to manually seek for the correct solution.

text = "yhql ylgl ylfl";
alpha = Alphabet[];
latinWords = WordList["KnownWords", Language -> "Latin"];
candidates = {StringReplace[text, Thread[Rule[alpha, RotateLeft[alpha, #]]]]} & /@ Range[26]
MemberQ[latinWords, StringSplit[#][[1, 1]]] & /@ candidates
Pick[candidates, %]

Second analysis

Here is another take on this, though it might be overkill for this problem. But let' s say that - for some reason - we don' t want to test all the possible Caesar shifts, for example if they are too costly

Then we can use the observation that the letter "l" is more frequent than the other.

text="yhql ylgl ylfl";
CharacterCounts[text]

What will be the first substitute to test, i . e . what is the commonest character in Latin?
Unfortunately, there is no letter frequency for Latin in WL.

LanguageData["Latin","LetterFrequency"]

So we have to roll our own, which shows that the character “i” is the most common character in Latin.

latinWords=WordList["KnownWords",Language->"Latin"];
CharacterCounts[StringJoin[latinWords]]

Let' s now use this knowledge to first test the shift that replaces an "i" with "l" :

alpha=Alphabet[];
shift=LetterNumber["i"]-LetterNumber["l"]
keytable=Thread[Rule[alpha,RotateLeft[alpha,shift]]]
StringReplace["yhql ylgl ylfl",keytable]

Which happens to be the correct shift and solves the problem.

Note : The shift found is - 3 which is the same as shift 23 (26 + -3 = 23) .

Addendum

Just to be a little more complete about this, here’s a brute force variant.

text="yhql ylgl ylfl";
alpha=Alphabet[];
Table[{shift,StringReplace[text,Thread[Rule[alpha,RotateLeft[alpha,shift]]]]},{shift,0,25}]// TableForm