Almost a Columbo episode Your task is to find two missing characters in a Columbo episode title.

Input

One of the 69 Columbo episode titles (as listed below), with exactly two characters replaced with *.

Example:

"Ran*om for a *ead Man"


Output

You may either return the full episode title, or just the two missing characters in any reasonable format, provided that the order of the characters is unambiguous.

Some valid outputs:

"Ransom for a Dead Man"
"sD"
["s", "D"]


Episodes

Please follow this link to get the unformatted list.

Prescription: Murder        Swan Song                             Murder, Smoke and Shadows
Ransom for a Dead Man       A Friend in Deed                      Sex and the Married Detective
Murder by the Book          An Exercise in Fatality               Grand Deceptions
Death Lends a Hand          Negative Reaction                     Murder: A Self Portrait
Dead Weight                 By Dawn's Early Light                 Columbo Cries Wolf
Suitable for Framing        Troubled Waters                       Agenda for Murder
Lady in Waiting             Playback                              Rest in Peace, Mrs. Columbo
Short Fuse                  A Deadly State of Mind                Uneasy Lies the Crown
Blueprint for Murder        Forgotten Lady                        Murder in Malibu
Etude in Black              A Case of Immunity                    Columbo Goes to College
The Greenhouse Jungle       Identity Crisis                       Caution: Murder Can Be Hazardous to Your Health
The Most Crucial Game       A Matter of Honor                     Columbo and the Murder of a Rock Star
Dagger of the Mind          Now You See Him...                    Death Hits the Jackpot
Requiem for a Falling Star  Last Salute to the Commodore          No Time to Die
A Stitch in Crime           Fade in to Murder                     A Bird in the Hand...
The Most Dangerous Match    Old Fashioned Murder                  It's All in the Game
Double Shock                The Bye-Bye Sky High IQ Murder Case   Butterfly in Shades of Grey
Lovely But Lethal           Try and Catch Me                      Undercover
Any Old Port in a Storm     Murder Under Glass                    Strange Bedfellows
Candidate for Crime         Make Me a Perfect Murder              A Trace of Murder
Double Exposure             How to Dial a Murder                  Ashes to Ashes
Publish or Perish           The Conspirators                      Murder With Too Many Notes
Mind Over Mayhem            Columbo Goes to the Guillotine        Columbo Likes the Nightlife


Clarifications and rules


Storing the list of episodes (or storing enough information to guess the missing characters) is part of the challenge. In other words, you don't get the list 'for free'. If using external files, their lengths must be added to your byte count.
Your program/function must support the titles exactly as listed above: 


The case of each letter must match exactly.
The punctuation must also match exactly.
Etc...

All characters in the title may be replaced with *, not just letters. They might be contiguous.
You may expect another 'blank' character instead of *, as long as it does not appear anywhere in any title. (If so, please mention it in your answer.)


Ah, there's just one more thing. I almost forgot...


This is code-golf, so the shortest answer in bytes wins!


Test set

Here is a link to all 14252 possible inputs.
Stax, 294 bytes

ê(·▲[*▬è0↑u■&lt;pá{∞♫┼¢]$Fò╦7τ}αzoN≡ù÷♥R♪♪éé#Ä↑♂¼y&gt;Φp←E├Tì·æ≈ßÿOÄ╩%▼Åp}∞½É÷_~ï♦♪s○╨5╓ok½♪◄}§3vimεå╩p"G4ƒs┬]K╡àbá•Ä◄E°µte\τ╚¥∞LH¥êoc▐I/♥∞íJ{σ↓⌠╢§♥]8&lt;µ6ε∩╠ån¥←A↨⌡↨▼♠áiåcTε╛ëJv⌡♂'┬û▒╟_E2↑☺│╚ê£-vç╘▬╒ì)#Φ¼∟æ∟q⌐▀☻7w_ì~Δc'Ω∙j≡┼Γó2"äV√n◘j≤╦╓ΘiL.,▲♂▌▼∞ccñó╘î←iaΩï)NCøAP┼b_ε☺Cam╥~αDM↕Aä&lt;₧¿é■N/╞τQ╠Γù&gt;b^S&gt;◘á↑


Run and debug it

Here's the approach:


Consider each possible substitution of characters for asterisks.  I'm using upper and lower-case letters, plus the handful of punctuation marks.
Interpret each candidate as a base-91 integer.  Why 91?  It covers the span of possible characters in the input, it's prime, and it's 2 digits.  It just seems good.  I don't know if it's optimal.
Modulus by a carefully chosen denominator. (359473561)  Then check for a the existence of the result in the embedded hash array.  If there's a match, that's the correct substitution.


In order to verify that no incorrect result would be produced, I used a C# program to verify the output for all possible inputs.  It's not very portable, and it's quite long running.  I've been letting it search for more than 24 hours for candidate denominators so far, so it's not really suitable for TIO.  I'm running it in LINQPad.

const string AllChars = " ',-.:ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
const int Base = 91;
const int InputCount = 14252;

void Main() {
    string filename = Path.Combine(Path.GetDirectoryName(Util.CurrentQueryPath), "titles.txt");
    var titles = File.ReadAllLines(filename);
    Console.WriteLine("Original titles: {0}", titles.Length);
    Console.WriteLine("All characters: {0}", AllChars.Length);

    var originalHashes = titles.Select(Hash).ToArray().Dump("Original hashes");

    BigInteger candidateMod = 359473561; //BigInteger.One * InputCount * AllChars.Length * AllChars.Length * titles.Length;
    BigInteger? bestMod = null;
    BigInteger failJump = 1;

    var candidateContainer = new DumpContainer(candidateMod).Dump("Candidate");
    var bestContainer = new DumpContainer(bestMod).Dump("Best");
    var rateContainer = new DumpContainer().Dump("Candidates / s");

    int tested = 0;
    var sw = Stopwatch.StartNew();

    while (true) {
        bool success = TestCandidateMod(candidateMod, titles, originalHashes);
        if (success) {
            bestContainer.Content = bestMod = BigInteger.Min(bestMod ?? candidateMod, candidateMod);
            candidateMod = candidateMod * 99 / 100; // reduce by 1%;
        }
        rateContainer.Content = ++tested * 1e3 / sw.ElapsedMilliseconds;
        candidateContainer.Content = --candidateMod;
    }
}

bool TestCandidateMod(BigInteger candidateMod, string[] titles, BigInteger[] originalHashes) {
    var moddedHashes = new HashSet&lt;BigInteger&gt;(originalHashes.Select(h =&gt; h % candidateMod));

    bool TitleIsOk(string title) {
        BigInteger originalHash = Hash(title);
        for (int i1 = 0; i1 &lt; title.Length - 1; i1++) {
            BigInteger placeValue1 = BigInteger.Pow(Base, title.Length - i1 - 1);
            foreach (var c1 in AllChars) {
                int offset1 = c1 - title[i1];
                BigInteger hash1 = originalHash + placeValue1 * offset1;
                for (int i2 = i1 + 1; i2 &lt; title.Length; i2++) {
                    BigInteger placeValue2 = BigInteger.Pow(Base, title.Length - i2 - 1);
                    foreach (var c2 in AllChars) {
                        int offset2 = c2 - title[i2];
                        if (offset1 == 0 &amp;&amp; offset2 == 0) continue;

                        BigInteger hash2 = hash1 + placeValue2 * offset2;
                        if (moddedHashes.Contains(hash2 % candidateMod)) {
                            return false;
                        }
                    }
                }
            }
        }
        return true;
    }

    return titles.AsParallel().AsUnordered().All(TitleIsOk);
}

BigInteger Hash(string title) =&gt; title.Aggregate(BigInteger.Zero, (a, b) =&gt; a * Base + b);


Another note about the storage of the embedded hash values in the program.  The hashes are sorted prior to embedding in the array.  I'm using a stax feature called "crammed integer arrays" which is a way of efficiently representing integer arrays.  This representation is able to store differences between elements if that's more efficient, so pre-sorting the values saves some bytes.

Here's the unpacked representation with some minimal comments.

Vl" ',-.:"+:2   all pairs of replacement characters
f   filter
 ;'*/s\$1T  do replacement
 91|E   base-91 digits
 359473561% mod by denominator
 "&gt;m\zR^ehBT}/!P*Nv:0N?s}FGN\:M.RT0r2VVm560$k;!yP&lt;I*&amp;R0_&amp;q6wEcg8)&amp;DsZ_!}&amp;}Y&amp;:Ngv&amp;B::n!g6y &amp;&amp;eej...Z-&amp;J..7!Rso36&gt;y89:*iA*eNHGBmZ5!kiZ5*D(};*Xgkvwqn^(g?!kJib&amp;g$ep!aDaA!Xw$_!w,2E.6NFG6DPat&amp;(u4?28BIRu&lt;bBL\S!ugPf!Ry;&amp;T0\/^y\S{gu;!\0spo!^3!}X0l!i$L5&amp;Z8@|!X4}p.LyU:X2M&amp;PR6Y!k*,[F(&amp;v&amp;(ZJ5Jo0d!RN!hsDFvV.kI*{u@_!a&lt;oGoqsG2}XhJ:2,"!
 #  count occurrences in crammed array