[Question]
  [
Correct a single-letter typo in a fixed text.
**Input:** The paragraph below but with exactly one typo, where a single letter `a-zA-Z` is replaced by a different letter `a-zA-Z`. To be clear, the letter isn't replaced everywhere it appears, but just in that one place. For example, the first word "In" might become "IZ". Non-letter characters like spaces or punctuation cannot be affected.
**Output:** The paragraph exactly as below, without the typo. A trailing newline is OK.
> 
> In information theory and coding theory with applications in computer
>  science and telecommunication, error detection and correction or error
>  control are techniques that enable reliable delivery of digital data
>  over unreliable communication channels. Many communication channels
>  are subject to channel noise, and thus errors may be introduced during
>  transmission from the source to a receiver. Error detection techniques
>  allow detecting such errors, while error correction enables
>  reconstruction of the original data in many cases.
> 
> 
> 
[TIO with text](https://tio.run/##dZFBUsMwDEWvogOU3oIFCw6h2kotxpaKZNPp6UFxEgaYIStH@v76enbKT1cZn/G9CLAsag07q0AvpPYAlAxJM8v1qNy5F8DbrXKaSo9rIWm30cnAE5Mkmvc6VYpGG7JLT0BmapCpU5pTNnuz/Td6myKpdNMKaBQ2qQi/D/KIgB1I8FIJjCrPQ47DB0UyXSDzlTtWyNgRNKow5Fv4KwukgiJU/QyvKI9/mjOAj8tbBISuRx1E2em0bVmGb6kdGj7gQgEkwueRKEMeNtkZijd2X80X07bSBNdhwSp8MdZJtK5xhuc/jH7sj7Xq/WiFrY9U9tknuBeOJQ9@30w3XL4OiMfqNnbUy4ygFsTkIBYv2SYMdPLzFw "sed – Try It Online")
The text is taken from the Wikipedia article [Error detection and correction](https://en.wikipedia.org/wiki/Error_detection_and_correction). But, your code may not fetch the text from Wikipedia or elsewhere, as per the [standard loophole](https://codegolf.meta.stackexchange.com/a/1062/20260).
Any site-default string I/O is acceptable. Fewest bytes wins. Please check that your solution works for all 23001 possible inputs, if you're able to run that much.
  
[Answer]
# [Python 2](https://docs.python.org/2/), ~~136~~ ... ~~97~~ 96 bytes
```
def f(s,n=0):k=s[:n>>7]+chr(n%128)+s[n/128+1:];return 0x2e838ca8118c7496+hash(k)and f(s,n+1)or k
```
[Try it online!](https://tio.run/##7VZta9swEP6eX3EYBnaSZXU22jQjhTI6CFsZbB2MlVIU@VJrsSXvTlqaX5@d7TprA/sH/mKke33uuQfhaudzZ6d7U1aOPBCyC6RxDLzjQXebMHoqTGl8fDB9/by8Xt7cf7u5/PBpDPF0OJyej1@nSTKQzCYBdSA2zraJ6clweJrsM1zDOuaxXZwk882Cb@f24uLsbqRziu2rdDpLRnxr38hhlM7v3hP6QBZOHqc4ezvTapamM3327vx0lCvO402ibNbWG6WJI9jstSPp7O/ZEywgWlowdu2oVF6ggM/R0Q7qLO0yYx86y9b4HFRVFUY3kSxpElJWwSMBa4NWY5PnsUBxlME@hY4BiaR3hl4a113a8g2O@iq@NkI768kVoAiljM6t@R2QBYLygFatCpQFFKY5ZHL4g4LMrSEzD8arAjLlFTixQrCHwBdYQOfKWix4AtfK7v7jbABwWP0SgOBdZwfrDMvqmynzwC1qhlLtYIVCiIDPgsYMskANd6Qsl4brLcOaXFmzCa1A6rpKxtFYjzGBqyOOns2visJtO5eU5aDzp95j2OZGhuz4O3Da0sV1A1mWp/BE9bqB4EgYsx1jssmyIUMx8iQaDNqaNxRwPpAhdvIFXhgry46jKBkAPmqsPFx9@djArv0rQrWRaBGV6E1iKqHAx/7fCRYLeKa@zgFRtI@WP3sZ9jJ8KcPI9G9TL4pjUVz2ouhFcSwK@Yv53ouiF8WxKPqXohfFQRQ/JtFf "Python 2 – Try It Online")
Simply replace each character in the given input with each possible letter, then check if the hash matches the correct string.
I use Python 2 `hash` function, which is deterministic and has no collision within our search space. 
[This program](https://tio.run/##dVNNb9swDL37V3ApBthxltXZ0KYZWmAYdii2XrbeiqBQZTrWIlOePpbm12eUbBfbgN0kke/x8ZHqj741tDqprjfWg0VngpW4AHd02XRbOvRWq075/OXp29fbu9v7x@/3Hz99WUC@ms9XV4s3VVFkjEwAlME6ZWgAVufz@UVxqrGBJncLuj4vNvtr97Chm5vLbSlbm9PrarUuSvdAb/lQVpvtB4s@WILz5xWu362lWFfVWl6@v7ooW@HafF8Iqge@siqMhf3JwTXMbgkUNcZ2wrMA8C0ae4SYK02taDe9HJRvQfS9VjJlOoZxStcHjxacVEgSE86jRg50gcbUBaC1XLFGjzJVGeitHa8cGzKkIW@NBmGRaWRL6mdAxxKEByTxpJFt1yodaj78QlZmGqjVTnmhoRZegOFXCPSS@JcWkK0gQu2WcCfo@J9gEuDC0w8WCN5M70BGOR546rINblDtoBNHeEI2hMXXQWINdbDJOyvIdcrF2UJjTRfdhGEtIq/gdiTGNpbw@R@P/uhfaG0OU4hpXZDtWHsBh1Zxk5N/L54OdrlYgIflbRitbpIEY9kxmhzjSXbJDOHQLWdZNq44w7hclp2xASj3wxyGDnRIdAdj9w54faJG5uFtyOJNRs4BvhS8HOpRo@dFcZsMUrqKCezODnONlLuiiBEA1bDJXPlBbZfKCd23Ih9DkNZDUcB0PQMPir2B3rC/cdBD@RjzvNn8X9QWSpZSRrr4R0bcDocuTPAMiJbwQKAJlCwaCFbM0OS@mEq1SKMJqpmAXH00fJLOsFdcedLbc/9@IgHAZ/7c8ZLuWYrCrDaEs9Nv) checks if the function returns correct output for all possible input. It takes ~2 hours to run on my computer
*Thanks @Jitse for saving -22 bytes with recursion!*
[Answer]
# MATLAB with Communications Toolbox, ~~88~~ ~~64~~ 59 bytes
```
Z=rsdec(gf([+input('') 879 323],10),532,530);disp([Z.x ''])
```
This uses a (1023, 1021) [Reed-Solomon code](https://en.wikipedia.org/wiki/Reed%E2%80%93Solomon_error_correction) shortened to (532, 530). This is a systematic, non-binary code with alphabet size 1023, which can correct any single-symbol error.
The number 1023 has been selected because, in Reed-Solomon codes, both the alphabet size and the maximum input length must be of the form *n* = 2*m*−1 for *m* integer. Since the input text has alphabet size bounded by 95 (number of printable ASCII characters) and length 530, *m* = 9 (*n* = 511) would be too small, and *m* = 10 (*n* = 1023) is enough.
The error-correcting code has first been applied to the input message to obtain the parity bits. This has been done offline. The submitted program only implements the decoder.
### Encoder
Each ASCII character in the input is considered as a symbol from a 1023-size alphabet, and then the code is applied using the [`gf`](https://www.mathworks.com/help/comm/ref/gf.html) and [`rsenc`](https://www.mathworks.com/help/comm/ref/rsenc.html) functions.
Note that the code is not part of the submitted solution, and is not golfed.
```
x = 'In information theory and coding theory with applications in computer science and telecommunication, error detection and correction or error control are techniques that enable reliable delivery of digital data over unreliable communication channels. Many communication channels are subject to channel noise, and thus errors may be introduced during transmission from the source to a receiver. Error detection techniques allow detecting such errors, while error correction enables reconstruction of the original data in many cases.';
x_ext = [zeros(1,491) double(x)]; % convert to code points. Zero-pad to length 1021
y_ext = rsenc(gf(x_ext, 10), 1023, 1021); % encode
y = y_ext(492:end); % remove zero padding
y = double(y.x); % convert to double
check_symbols = y(numel(x)+1:end)
```
This gives `check_symbols = [879 323]`. The encoded codeword is the input expressed as code points with these two numbers appended.
### Decoder
This is a program that reads the input text as a string from STDIN and displays the corrected text in STDOUT.
```
Z=rsdec(gf([+input('') 879 323],10),532,530);disp([Z.x ''])
```
The parity-check symbols are appended to the code points of the input, and the result is provided to the decoder with code parameters `532`, `530`, which define the shortened code. This causes the decoder to implicitly assume zero-padding. The `gf` and [`rsdec`](https://www.mathworks.com/help/comm/ref/rsdec.html) functions are used for this. The result `Z` is converted to char.
Note that `+` is used instead of `double` to save bytes; and similarly, `[... '']` is used instead of `char(...)`.
### Example run
[![enter image description here](https://i.stack.imgur.com/rvcby.gif)](https://i.stack.imgur.com/rvcby.gif)
[Answer]
# [Jelly](https://github.com/DennisMitchell/jelly), ~~30~~ 25 [bytes](https://github.com/DennisMitchell/jelly/wiki/Code-page)
```
JœPjⱮØẠɗ€⁸Ẏ1,ȷ9ḥ⁼“×Ṙ*-’ƲƇ
```
A full program printing the result (as a monadic Link it yields a list of one list of characters).
**[Try it online!](https://tio.run/##dZE9TgMxEIWvMjUKkSg5AAVISHTUjj3JOvKOw9gm2o7Q0NFQBCQKGjqEBA0/okNEcIzsRZaJN4kAic6eeZ557/MQnauaZu/j4mBYP9y/X85fb76m9eldPXmev55vdT6ftufPt/XkrT65fp/OXy43NuuTq9nj7Kxpml0CS33PpYrWE8QC/WEFigxobywNcoUrGNtYgBqNnNVZGeSZSMpRisgQtEXSmN9FdCiNMtFS2gFk9gwGI@q8pR3PvLxKr1VoT5G9A8UoY3RB9ihhEAsqApLqOQRGZ/PByOEYxZnvg7EDG5UDo6ICL1VItBb@8gK6UEToQhf2FVX/NLOBkHpDMQjRr@pA3gbstCmLFFrXAUpVQQ8FiJg3SaMBkzizY0WhtCEshvfZlwuaEHxiYSVzlcTRuIjRhZ0/jH7kV8758aolY0PSxXJ3B8aFlZArfmumLa6wWCCfFTktUfezBc9CjFbE5CfLDEMFDN1v "Jelly – Try It Online")**
### How?
Changes each character (including non-`A-Za-z` ones) to every character in `A-Za-z` and checks if a hashed value is equal to that of the required input. The hash function was chosen so that none of the \$450 \times 51 + 79 \times 52 = 27058\$ possible states for each \$451 \times 51 = 23001\$ inputs hash to the same value as the expected output\*.
The program to check for collisions is [here](https://tio.run/##pVVtU@M2EP6eX7HNzI3tizEJF0KamTBDKe3RO6Al9PrCMR7FVmIZWzKSDKSd/na6kuzwdlyZqWaSSNrVo91H@2yqlc4Efzeu5N0dKyshNWREZQWbdzopXUDml2TJkhCuSVHTEHgw6QAOuwu9KQzsUmXkksK0PRvZdby1PfIlraRvDwcR5YlIqe/VerEx9oIgStmSKu1vD7YCC1MIvlQIc864jhZSlPF8panyLdw5gwkw6MH4IgSvYFoX1AtgISTuMg6S8CX1@yEgXAjj4MJCSqpryUHVpe@7XGB3F1gAG/BgiaiDIIC3LoJzdvEUdjRE8xsYjIfD0c5w2N95t9P/dnt7MBqM8BQ3IKNhp8MWEMeclDSOYTqFbhyXhPE47jrWZphb95AjLMKXRDPBQWdUyBUQngKyw/iy3blhOgNSVQVLrKcy0SSirGpNJaiEIZ3UntO0oGgoa964hkClxARSqmlib3HwUjZLtDmPRHAtRQFEUoRJMs6uaqowBKKBcjIvKDJYMDtJcXJNMTKxAHw5pkkBKdEEBO5CzdeOj2KBJCOc00JFcET46gWjDUDV8xwDBC3afeCCKSw7m2VWKxe1wvJbwZwiIRh8Wic0hbSWljt8LlUypQy4qSDDJihRS@QKcQmmk1CTRgQHTzh6kD8pCnHTmhBW1UnW3B3CTcYwyZa/NaeOLmUuwMfSsm6oXtgQhETGeMsYvmRpySCKqqhri@Pj4ewsNhVSMNTEzCli//3eqdnz9r7b//7ghx/fH/704ePR8cnPv5zOzn799Nvvf/xJ5gkqdZmx/LIouaiupNL19c3t6i/vHiKeHZwhjKLat2uHfgStgI/NrN@Ojt0zYj6lVzWTND2pNdadUbh/FDahhnDsYCqkXvtektHk0rKFX6YMMiJJYoo1ZYsFlaZeVRRFnjv1RGEF5Zh0015ae2IdbMT3BjNQaGhDic1QrBNbxozX9JHPLYbrIj2foKJ7cJ7Yn2bPin5y8RTWJnhrcjPwzzl4HIdtMQRrFD6ZHmdLyve8KBeM@7dB0PkiQamo5y8T1PCjhRHYFBpmsGENbIdyq03Ysl7zCl2c66Z7QbudiJqb55pX@82s/zLpFvox8fm9D@sNQnj2Og1bLf6uueo5NS6M3jqOZw4NM5/l3/1JtLX4540Xudbou6NvXU6wufkwyQAbHE@nXsPUw3Gf8IbfzrFvP3ZsDev/r1fV3Gvrbg2UfwWoBcsdWP41sMbXXIkNUa9RrahNazTnn1n@A/BV6sidIX9gyL8gm/8lnxdlxELIQ6QbP/j7gqi6n@XeyYdvukHn7u5f "Jelly – Try It Online") (I split the 2-char check into 5 parts and ran for over 1h to completion).
\* smaller hash functions might work for just visited states which could save bytes (although if a collision does exist further on than the correct match then an `Ḣ` must be appended).
[Answer]
# [C (gcc)](https://gcc.gnu.org/), ~~260~~ ~~259~~ ~~247~~ ~~226~~ ~~223~~ ~~217~~ ~~215~~ ~~214~~ ~~198~~ 189 bytes
```
y;*R=L"ė|h0\"k5&j4Đzm7ſ4sM6~0=a[z@z4zp4y j,-#3#mu7qĘdd";char n,k,t,u,r,c,x;f(char*s){for(n=y=23;n--;c?u=n:0,r?x=r,t=n:0)for(r=R[n],c=R[n+y],k=y;k--;r^=s[y*n+k])c^=s[y*k+n];s[y*t+u]^=x;}
```
[Try it online!](https://tio.run/##7VJNbxMxEL3nV4xaCSWNkxaaUonVqnDgUIlK0CMhlRyvk3V3d5z6o8kmlN/ADf4Nl/K7wti7LQXEiRtqDpuRPfP83psnBnMhtts62TtP3@zcfvmYH3zYKY6eXI5uP6@r4@/fRvbs@aeDlI/XL9ej9WJUwyUb7B7uVv746vZrlu0kIucGkBXMMc8ME2yVzLrhcM/2NjNtupjW6bPDBAeDRJz4FF8cMHOySg1zoe6FFpOej3HCRPjr1xNWpHVSUL@5SO243sN@MemJpi76OElC4fp@cpGukputQgcVV9i91irrdTYdiJzs@Ojw6STpwDJXpYTuXDrbtb0ewP4@nEuegUYJChfeQamo5A44OFXJDtBvE7@gZtAlJbEOg6/KUi9hWnIs4pQlBHB5C8SA5AAqIU2oKu6cwjnoWWxx0rphAwswIy7JT9jFoqxBGkPjQhsjhVOagDXYYacZIXh7P0Mjb00Q3g3AKJdwzUsv6aVemAgtN52b7SkSr4ZIhMulNjVwzOiRLDBrT5bK5cCJgxKxM6p6ryt6k5RYoSQKGeecLKXQVeWxbWUt60y6lnQDf6@B7u50oTO6BG6CFSJHdeXJP5eT8RL5lJZkZKlikVFxLYkZWZepuXK8hIw7DppOweN94y9cwuIRZWmHcMax/stlJGD99JIIBofbc0CtrGSNytzbhrWlaNUwDfsl8pkXMoPMm@id4WgrZW0AnxldxSVb7Q15Rbic5AgZZAzh9W8ePdDPY6LaK4K1XuTt26zN7h@5aOyy4QFaljO@tbrJmTbkGN45RpusohncyhCm1b9kQjxm4n/MxOljJh4z8SAT74Y/AA "C (gcc) – Try It Online")
---
*Now with 9 fewer bytes, thanks to @ceilingcat!*
---
*Thanks to @Bubbler for golfing 16 bytes off!*
*I haven't updated the readable version to include @Bubbler's modifications, but the biggest improvement is in the XOR computation loop. The shorter approach starts at the desired values `R[n]` and `R[n+23]`, and repeatedly does an XOR, which will yield `0` at the end (except at the bad character). My answer had the loop starting at `0`, and checking for the values `R[n]` and `R[n+23]` at the end.*
---
**I've added a readable version of the code, commented and with whitespace, at the end of the answer.**
---
This doesn't use an md5sum or hash function, since that's not available in C without using an external library. Here's how this program works instead:
Imagine the input text, except for the final `'.'`, arranged in a square. The XOR of each row and the XOR of each column are computed, and they're compared with the correct XOR values from the original text. One row XOR value and one column XOR value will not be what they should, and that tells you which character to fix, and which bits to flip to make it right.
---
---
Here's the more readable version of my last version of the code (before @Bubbler and @ceilingcat's improvements), commented and with whitespace:
```
f(char*s)
   {
/*****
   The original text contains 530 characters.
   Using the fact that 23 * 23 = 529, view the text as
   arranged in a 23 x 23 square (plus the '.' at the end,
   which doesn't matter since it can't be changed).
*****/
// The string R below is 46 characters long.
// It consists of the XOR of each of the 23 rows in the original text,
// folllowed by the XOR of each of the 23 columns.
    char *R = "\x17|h0\"k5&j4\x10zm7\x7f""4sM6~0=a[z@z4zp4y j,-#3#mu7q\030dd",
         n,k,t,u,r,c,x;
    for (n = 23; n--; )
       {
// Compute the XOR of row n of the input string, and store it in r.
// In the same loop, compute the XOR of column n of the input, and store it in c.
        r = c = 0;
        for (k = 23; k--; )
           {
            r ^= s[23*n+k];
            c ^= s[23*k+n];
           }
// If the computed row value doesn't equal the row value from the original text,
// save the bad row number in t.  Also save (in x) the bits that are wrong.
        if (r-R[n])
          {
           t=n;
           x=r^R[n];
          }
// If the computed column value doesn't equal the column value from the original text,
// save the bad column number in u.  There's no need to save the bits that are wrong,
// since they're guaranteed to be the same as the bits that are wrong for the row.
        u = c-R[n+23] ? n : u; // Golfed equivalent of:
                               // if (c != R[n+23]) u=n;
       }
// Fix the bad character, which is in row t and column u, by flipping the bits in x.
     s[23*t+u]^=x;
    }
```
[Answer]
# [Ruby](https://www.ruby-lang.org/), ~~122 121 119~~ 113 bytes
```
->s{s[a=(0..9).sum{|x|",JEDI>(:6#"[x].ord==(0..529).sum{|w|s[w].ord*w[x]}%84?0:2**x}]=""<<50998-s.sum+s[a].ord;s}
```
[Try it online!](https://tio.run/##dZJRb9MwEMff8ylOQUht6aKsULSOZRUSe9gkvgBRHxz7shgldvDZpGXtZy9Xt5kAiSfbd3/f/e9nu1DtjnVxvLqnFypFMcmzbDXNKHQv@@0@nT89fHm8n9x@fJOW201mnSqiZLkYRcOeyiFmZgNLDm9vPqzz28Vstj1sijS9u1vmq9XNFZ3U77hDlH6iw5HyIn00oE1tXSe8tgZ8g9btQBgF0iptnsfIoH0Dou9bLaOS@BpLuj54dEBSo5EY73lskRNdMBfpHNA560ChRxm7nMs7dzly7qyQ1nhnWxAOuYxsjP4RkNiC8IBGVC2Cw1bHjeLNT2Rntgaln7UXLSjhBViOQjCvwr@8gGyEMdhSBl@F2f0nGQ1QqL6zQfB2jIOxmnB@nrIJdHZN0IkdVMhA2LwKEhWo4CI7Jwx1muhUvHa2O9EEssExK64reByJpzEyePiH0R/zi7a1w5jishRkc@k9h6HRPOTI75XpGRedGvBjeRcuqOtowTomZkZi/JJdhCEIKUuThK4LyrMqobzsgWdQk@X7fLop@kk5W3/OsvW3@WwteP21yUh0fYvTJOGfQEB5QouiLinfXAKLcS0Kuj7@Bg "Ruby – Try It Online")
### How:
Compare partial sums applying different masks to the string:
```
In information theory and coding theory with applicati [...]
X X X X X X X X X X X X X X X X X X X X X X X X X X X  -> "I nomto hoyad [...]"
XX  XX  XX  XX  XX  XX  XX  XX  XX  XX  XX  XX  XX  XX -> "Innfmaonhey d [...]"
XXXX    XXXX    XXXX    XXXX    XXXX    XXXX    XXXX   -> "In imatiheord [...]"
```
With 10 partial sums (and the total sum of the original string) we have enough information to detect and fix a single error.
Incidentally, the partial sums modulo 84 converted to ASCII form the word "JEDI" and an Australian smiley, which I hope bring some imaginary bonus point.
### Why not hash?
From the Ruby documentation:
***The hash value for an object may not be identical across invocations or implementations of Ruby. If you need a stable identifier across Ruby invocations and implementations you will need to generate one with a custom method.***
I'm lazy. That's why.
[Answer]
# [Python 3](https://docs.python.org/3/), 83 bytes
```
def f(s,t=118,u=-10340):
 for x in s:t-=x-96;u-=t
 *l,=s;l[u//t]+=t;return bytes(l)
```
[Try it online!](https://tio.run/##dVTLbtswEDxbX7F1D5YayYmTog8HOgVJkUMvdQ8FDMNgqFXEliJVPpr469Ml9UicogcB1HI5Mzu7ZHdwjVYXT08V1lCnNnflavUp92WxOrt4f5atE6i1gUcQCuzaFeVj8fnDpS9Kl8A7mZf2Um796anbnZTu0qDzRsHdwaFNZfa0Kee3ik4SQsuc0Apcg9ocgKkKuK6Euh8jD8I1wLpOCh4zbSDkuu28QwOWC1Qc4zmHEmmj9WpIzQGNIY0VOuSRpYc3ZvilvT6Da@WMlsAMEgxvlPjt0ZIE5gAVu5MIBqWIi4oWf5CU6RoqcS8ck1Axx0BTFLyaEo@0AG@YUijtEr4ydfjPZhRg/d1PEghOj3FQWljM@yobb3vVFlp2gDskQ0h85TlWUHkTvTNM2VZYG8Bro9vgJljtDXlFuIzK4RjKWML1K49e1M@k1A/jFsFaz5uBO4eHRlCRo3@Tp71dNhBQs5zxg9V1lKANOaZGx6iTbTSDWbTLeZKIttPGAR0juiR5C9@wk4w0kxGG8dBx6kinrYigop8OnoQZbdkv3LtDp2lWRc5pQGfD2NntWuzgBDh9ditOVutdEsC/oELDHOkPk0zFglZYPFMFMDsqvye31KjsLdzWINFRlt1rJQ95yKEqaTnEC6eLfjXgPAgioG5VRBIFE2HUa0nwEVj53XgM@sUxCUVmkzoL5aBmyegWiP2QmcxojvCf1MVi@VMLlW55Y1K6YdjRn8ti5dNvaAhNzj2mF@c5rM4/ZruMgJJZyBL5VdhH5dvgGqY2SIxbPN7JiS6EZ4ZIX7ckxKmm9Oo4P4uDnRp4QyVF0NnsIFBWYEKbvqN1NLG1V8OAhlspFD0ANh7U3vXr@L4UQ4uCw/TauLQOiEFlfDmeTd9EJrsfZpcuTxneuSW9J@RHmgW1JJbg0hdJOSy8q4tPiyyo3fSl9mN2w8j4aehCD5OkIy0unYcSQqeWy3mWBFV7g9ZLR5SDxiFzO79hdK2qeQ7zjeccsaKf3fbFGWpJEirAat@XSiDp4vbH4mSzPV/vsqmAZBL9nFinx0ezSeL1I2s7ib2x6/ko6Ch9DGavD/UteD71mjlLnv4C "Python 3 – Try It Online")
We compute a custom numerical checksum that let us locate and fix the error. This means don't need to try possible changes until we find one that works. As a result, the code run very fast, letting us test all 23,001 possible inputs in under 2 seconds in the linked TIO.
Input and output are as bytestrings, which requires Python 3.
**The checksum**
It's a bit easier to understand the idea using the code below.
```
def f(s):
 t=50998-sum(s)
 k=13447420-sum(i*x for i,x in enumerate(s))
 i=k//t
 l=list(s)
 l[i]+=t
 return bytes(l)
```
[Try it online!](https://tio.run/##dVRNj9MwED3Xv2IohyZsWvYLAZVyQoD2wIVyQKqqyutMNmYdO/iD3f76Zex87LaIQyRnPH7z3puxu4NvjL56eqqwhjpz@ZqBL9@df/z4YelCSwEG9@XF1fX1@@vL8xSSbx6hNhZk8QhSA@rQouUeKZeSZXn/9q1noEolnU/n1VbuzkqKWfTBarg9eHSZyp825fxGEwahtdxLo8E3aOwBuK5AmErquzHyIH0DvOuUFCnTxdLCtF3waMEJiVpgOudRIW20QQ@pBaC1xLdCjyJV6eGtHX5pr88QRntrFHCLBCMaLX8HdESBe5LJbxWSBCXToqLFHyRmpoZK3knPFVTcczAUhaCnxCMuIBquNSq3gm9cH/6zmQi4cPuLCII3Yxy0kQ6LXmUTXM/aQcsPcItkCJGvgsAKqmCTd5Zr10rnInhtTRvdBGeCJa8Il5McgVHGCj6fePRCP1fKPIxbBOuCaIbaBTw0kkSO/k2e9na5WICa5W0YrK4TBWPJMT06Rp1skxncoVvNGZNtZ6wHOkblGHsN37FTnDiTEZaL2HHqSGecTKCynw7B4gy3/B73/tCZzBWyEDTPs2Hs3HYtd3AGgj63lWcX6x2L4F9Rx/kl/nGqSSwYjcvnUhHMjczvyC09MnsNNzUo9JTl9karQxFzSCUth/jSm2W/GnAeJBWgblVUJBGmgomvI8JHYOUPGzDyl8dFKDKb2DkoBzYrTrdA7odMNqM5wn9SF4vVLyN1thWNzeiGYUd/Pk/Kp9/YEJqcO8yuLgu4uHyf73ICYrP@1n86vfWxSgJId3IqF8MzS0VPWxLjpCn7dJyfp8HOLLwiSQl0NjtIVBXY2KYf6DxNbB30MKDxVkpND4BLB03w/Tq9L8uhRdFhem18VkfEyDK9HM@mb1Iltx9mly5PGd/BFb0n5EeWR7ZEluCyF0kFLIKvlx8WeWS76aX2Y/aFk/HT0MUeMtYRF5/No4TYqdVqnrPIam/RBeWp5MBxyNzOv3C6VtW8gPkmCIFY0c9u@@IMtYRFBVjte6kEki1ufi7ONtvL9S6fBLCJ9HNinR0fzSeKnx952ynsjV3PR0JH6WMwPz3Ut@D51GnlnD39BQ "Python 3 – Try It Online")
The core is the checksum, which gives two values. The correct string (and only it) gives the values `(50998, 13447420)`, which are used in the code above.
```
def checksum(s):
 t=sum(s)
 u=sum(i*x for i,x in enumerate(s))
 return(t,u)
```
[Try it online!](https://tio.run/##dZKxctswDIZ3PwUuk9XTeemWu4wdOnTKE9AkFLElAQUA6/jpHYiyfG3vuoHAL@DHBy1Xm5m@3m4JJ4gzxl/a6lGH5wPYyxYeoPUof/mAiQXy@AGZAKlVlGDoEtcIWhM62tiG2@vL03dyjatrsMwENiPLFQIliJwyve2ZS7YZwrKUHLtS19aR69IMBTRmpIj9O8OCXqiN7tIRUMT9JDSMfcrWXuT@9NqmiEwmXCAIeps4U35vqG4hmK8RzgXdfsk9SB78RnfGE6T8li0USMECsGeh0UP4lxdHF4iw6Al@BLr@p9gNaDv/dINgvOeBOCuO25Zz0821Qg1XOKMDcfOpRUyQmnR2EkhrVl2bT8J1pQnKTZyV9w2@TsR1jRN8@4fRH/uHUviyl7yttjjfZ49wmbMvufN7MN1w6TrAj2XS7qinboHFidFOzC9ZO4ygqKenw2Fx93Z8/GWvJz8uJzwOw3D7BA "Python 3 – Try It Online")
The checksum not only lets us know whether the string is correct, but the difference from the checksum to the real string lets us deduce what the error is and fix it. This doesn't rely on the typoes being with letters.
**How does it work?**
The checksum consists of two parts, each of which is a number.
The part `t=sum(s)` is simply the sum of the (ASCII) values in the string. Given a string with a single typo from the original, we can figure out the numerical difference between the original and typoed character by comparing the sum to the original's sum of 50998. For example, if we find a sum of 51001, we know that the typo increased a character by 3, such as `a->d` or `M->P`.
So, we know the amount that the character is off, but not which one. That is, we don't know the index. For that, we use the additional information given in the second value `u` of the checksum,
```
u=sum(i*x for i,x in enumerate(s))
```
Here, we add up all the characters, but each one is given a multiplier equal to its index, so zero times the initial character plus one times the next one, up to 529 times the last one.
The idea is similar to a classic puzzle:
> 
> You have 10 bags of coins. Each bag has 10 gold coins that each weigh 10 grams, except one bag is filled with lighter counterfeits that each weigh 9 grams but otherwise look normal. You have a digital scale that displays exact numerical weight. How can you determine which bag has the counterfeits, using only a single weighing?
> 
> 
> 
The solution is to number the bags 1 to 10, and put 1 coin from bag 1 on the scale, 2 coins from bag 2, up to 10 coins from bag 10. That's 55 coins total, so if all the coins had been real, they'd weigh 550 grams. But, each counterfeit coins lowers the total weight by 1 gram. So, since the fake bag will contribute its index-number of underweight coins, which is however many grams under 550 we see. This tell uses which bag is fake. (We could also have zero-indexed 0-9 and used 45 coins.)
The checksum `u` works just like this. Suppose that the typo is index `j`, and it's off by an amount `d` as determined by the first checksum value `t`. Then, the index `j` will contribute to the error in `u` with a multiplier equal to `j`, so it will be off by `j*d`. Knowing both `d` and `j*d` lets us find the typoed index `j` by dividing.
**Fixing the error**
The error is fixed as `*l,=s;l[u//t]+=t;return bytes(l)`. This converts the string to a list, modifies the indexed value in place with `+=`, and then converts back a bytestring. This is pretty ugly. Note that strings and bytestring are immutable in Python, unlike lists, so we couldn't just modify the string directly.
I'm still not really clear what default string I/O permits, despite having given it the go-ahead in the challenge. If we can do input and output on lists of ASCII values, then we can save a lot of code, especially by modifying the input list in place.
*61 bytes*
```
def f(l,t=118,u=-10340):
 for x in l:t-=x-96;u-=t
 l[u//t]+=t
```
[Try it online!](https://tio.run/##hVLLbtswELzrKxa@xEZkJ26KInGhYw459JRLgSAw1uQqYsGHSi5r6@vd1SuoixQ9idqZnZ0dsu24Cf7ufNZUQ720JVfb7X2Zq/X29u7z7WpXQB0inMB4sDteV6f1w5eveV1xAfYl39zw63XF5@dq8eSFI1yHbIIHbijEDtBrUEEb/zZXjoYbwLa1Rg3M1Eur4NrMFCEpQ17R0MdkSQCX/UQtgWIUN5qY1DBllI9x@hVsZKjgOQYLGElkVOPNz0xJLCADeTxYgkjWDActh18kzkIN2rwZRgsaGSFIFbJ/J154AdWg92TTBr6h7/4BDgZSPvwQg8BhroMPJlE5btnkNLpO4LCDA0kgYl5nRRp0jkN2EX1yJqVevI7B9WlCCjlKVqKLso6ifo0NPP6V0R/7o7XhOEMim7JqptklHBsjS875vWc6xpX6AXJZHPMUdT1YCFES83NicpNuCAMTpc2iKLhrA@m9tPXjKrh6@n4F1/D88mn3egnuMe2tSSyk/rN02C5D1OUFabUqilYOvFw8ntC1ts9KHg5gGrp2i9WEf6g9g6JSf8yA//SffwM "Python 3 – Try It Online")
**Optimizations**
```
def f(s,t=118,u=-10340):
 for x in s:t-=x-96;u-=t
 *l,=s;l[u//t]+=t;return bytes(l)
```
We save some characters in the above code with a few optimizations.
Instead of computing the checksum values `(t,u)` and subtracting the target values, we get the differences directly by initializing `t` and `u` to to their target values and subtracting the contributions for each character iteratively. Passing in the original string would make them end up both as zero, giving a divide-by-zero error in the code.
Instead of using `enumerate` to compute the weighted sum `u`, we repeatedly subtract the running sum `t`, which subtracts each character value successively more times. This effectively computes the weighted sum for the reversed string, that is with the biggest weights at the front. But we end up with a negative index that goes from the back, so it works out.
Finally, we are able to obtain shorter-to-write checksum values `(t,u) = (118,-10340)` instead of `(50998, 13447420)` by using a slightly different checksum that first subtracts 96 from each ASCII value. This is the rounded average ASCII value for the text, so subtracting it makes the string values average out to roughly zero. This make the checksums have both positive and negative summands that make them close to zero. These values of `(118,-10340)` are tantalizingly close to being one digit shorter, and one is negative, but I didn't see a way to shorten them.
I was originally planning to do all checksums computations modulo some prime to make the resulting values shorter to write, but they are short enough anyway that it's not nearly worth it, even without the optimization above.
[Answer]
# [JavaScript (Node.js)](https://nodejs.org), 78 bytes
This is a port of [xnor's great answer](https://codegolf.stackexchange.com/a/201032/58563).
```
s=>(t=118,u=10340,g=k=>Buffer(s).map(x=>(u+=t-=k&x-96)-531*t?x:x+t))(g(~0))+''
```
[Try it online!](https://tio.run/##dZFBb9swDIX/CtHDai@OkaDrsBZwBxToYYfdd1Uk2lYrUx4pNcllfz1j7LhYB/QmkU/ke5@ezasRy35Ma4oOT21zkuahSM12@63KzXZz82VTdc1L8/CY2xa5kLIezFgcVJRXTVo3L58O67uv5fr2Zvs5fT/cH1apLIuu@LMpy9X19clGkhiwDrEr2uLqB8EvaiMPJvlIkHqMfARDDmx0nrqlsvepBzOOwdtJKeBJJcOYEzKI9UgWp3cJA2pjyHSRVoDMkcFhQjttmcczX67amxXqLXEMYBh1jO3J/84oasEkQDK7gMAY/HRwenhFdRZbcL7zyQRwJhmIWoVMb8J3XsD2hgiD1PDT0PGD5mRA8u5ZDUKKSx0oesFqTtlnmV0LDOYIO1Qgat5liw5c5okdG5LBi5yHtxyHM02QmFlZ6VyjcSyeY9Tw9B@jf/KbEOJ@aelYyba/7K5g33sNufB7YzrjkvMC/azE@YK6nSxEVmK0ENOfHCYYRlDqq7I8/QU "JavaScript (Node.js) – Try It Online")
---
# [JavaScript (Node.js)](https://nodejs.org), ~~121 118~~ 116 bytes
```
s=>[...s+'jEIM'].some(c=>[...s].some(C=>Buffer(o=p+c+s.slice(++i)).map(c=>k=k*99^c,k=p+=C)|k==0x6B9400B8,i=p=''))&&o
```
[Try 10 random test cases online!](https://tio.run/##dVPbctMwEH3vVyx9qO06ddNOYSjFZUgboEC4hXsmzCjSOpZjS64kN00v3x7WjlOgMzxJ2ss5Z4@kjF0wy40s3Y7SApdJvLTx8SiKIht6Wf9s4I0jqwv0eRttjyfxca9KEjS@jsuQhzayueToh6EMgqhgZd0wi2fbh4e/eGdGNfFJcDOL4@7lo97hQbfbe9yRcRl7XhBsbenlEGLYPFMgVaJNwZzUClyK2iyAKQFcC6mm68hcuhRYWRJjU2mpjUqKsnJowHKJimPT5zBHShSVaks7gMZoAwId8oZlBW9Me6TcqoJr5YzOgRkkGJ4qeV6hJQnMASo2yREM5rLZCNpcICnTCQg5lY7lIJhjoCkKlbor/EcL8JQphbmNYMDU4j/JRoCtJhkJBKfXcVBaWuyspkwru1JtoWALmCAZQuJFxVGAqEzjnWHKFtLaGjwxuqjdBKsrQ14RLqNxONZjRNC/59Ff87M81/N1imBtxdOWuwPzVNKQa//uPF3ZZWsCuixnqtbqpJGgDTmm1o7RTRaNGcyijTaPNjboQfg5OpD0RPaOaHlKa5c2YRjA9QaA0HANGWUHzKURjSl04QewDcMoRzWlt3IDVH@7kuc/2B2xnavxrowcWucPR9k4CI7WOLx@iGzCBSbTVGazvFC6PDfWVRfzy8XV897Jaf/Fy1dnr9@8Hbx7/@Hjp@HnL1@/ff/xc3N0n/7hfk08/sNM2DE0fDWdJaZh@226HcgCCIk9vItltN9rKmvXdI5Rrqe@v043bAW7bHphhywJWhBv5LVQ3ti7D1gX07JHv7Rkoq@Ev39AjYlvg0YdPAOvZNai8OAJeAkj4cIjGbfL3w "JavaScript (Node.js) – Try It Online")
## How?
### Replacement letters
All letters in the original paragraph appear at least twice, except ***j***, ***E***, ***I*** and ***M*** that appear only once and may disappear in the modified paragraph.
That's why we do:
```
[...s + 'jEIM'].some(c => ...)
```
This way, it is guaranteed that each possible replacement letter is tried.
### Hash
Using a built-in hash would be rather lengthy in Node:
```
require('crypto').createHash('md5').update(string).digest('hex')
```
Instead of that, we use the following custom 32-bit hash:
```
Buffer(string).map(c => k = k * 99 ^ c, k = 0)
```
This was (rather painfully) tested against all possible inputs.
[Answer]
# [Perl 5](https://www.perl.org/) `-MDigest::MD5=md5_base64 -F`, 86 bytes
```
map{//;for$j(a..z,A..Z){$_=$j;(aNCL7lqAQsi7llQYWh7r8A eq md5_base64@F)&&say@F}$_=$'}@F
```
[Try it online!](https://tio.run/##dZJvaxsxDMa/il6E/oH02sGyjJZCQtvA2DLoq7G9KYqt5Bx89kWyF7LSr76r7pJru0FfnU96LD36yTWxHzVNhfXj@fnVMvJgfYJF8Wc4LYpfp4@Dh@vB@uoEv998G/vN9F7c2Pv7nz/KMX@eAm2gsqOHBQp9@jiZnR4dCe4ms6f21vHTZNY0XwK4oFUrTC4GSCVF3gF@tWCidWHVR7YulYB17Z3plKLXVFLVORGDGEfBEGCwkMiTJqocDtIhEHNksJTIdF1amYnMh1/N7RUmhsTRAzJpGVMGt8kkagETUMCFJ2DyrjtYPfwmdRaXYN3KJfRgMSFEjUIOL8J/vIApMQTyUsAcw@6dZGdA8mKtBiHFPg4hOqHhfsoyy961QIU7WJACUfM2G7JgM3fsGINUTqQtvuRYtTRBYmZlpXVRxzHUjlHA3X@M3syP3sdtn9Kykk156D2Ebel0yJ7fC9M9Lmkb6LIS5wPqZWchshILPTHdZNXB0Gcixd9Ydwtuzuaj4uLDhX5v3YokXV7Ob0fXr@@pOZs9Aw "Perl 5 – Try It Online")
Brute force method. Runs each character position through all possibilities until it finds that the md5 checksum matches the original message.
[Proof that no other single character change matches the checksum.](https://tio.run/##dZJha9swEIb/yhFC1kLqdrAsoyHgsM4wWAb9NNYv5SxdYgVZck7SQlb61@ednbjdBvtk@e703nvPqSG2s7bNy2VeLGpsnq6vFxvP490FZtnP6SrLHi6fxo/L8W5xgV8/fpnb/eo@mLm199@/VXP@sALaQ61njyUGev8uLy4nk1FejhyN8mI0mQQ85sVzp/DmOS/a9rMD46RDjdF4B7Eiz0dAp0F5bdx2iBxMrACbxhrVVwa5JiV1kyIxBGXIKervRbIkiTq5c@kUiNkzaIqk@i4neebzr@ROFcq7yN4CMomMqpzZJwpiASOQw9ISMFnTH7QcfpA48xvQZmsiWtAYEbxEIbmXwr@8gKrQObIhgzW643@SvYGQyp0YhOiHODhvAk1PU1YpnFwHqPEIJQkQMa@TIg06cc@O0YXahNCJb9jXHU0IPrGwEl2UcRR1Y2Tw6R9Gf8yP1vrDkBLZkFR17j2FQ2VkyIHfC9MTrtA1kGVFTmfUm96CZyHmBmKyybqHIU8mZL980y@4vVrPspu3N/K9M1sK8fZ2fTdbvr6t9qr4DQ "Perl 5 – Try It Online")
[Answer]
# [C (gcc)](https://gcc.gnu.org/), ~~181~~ 163 bytes
```
n,*R=L"[z@z4zp4y j,-#3#mu7qĘddė|h0\"k5&j4Đzm7ſ4sM6~0=a",u,v,x,y;f(char*s){for(n=529;n--;s[n]^=u&v)for(y=23,u=R[n/y+y],v=R[x=n%y];y--;u^=s[n-x+y])v^=s[23*y+x];}
```
[Try it online!](https://tio.run/##7VJNb9NAEL37V4xaUSWpk5Z8UCHLEhw4VKIHeiSk0ma9ibe1Z9P9cOOU8hu4wb/hEn5XmF07VUHixA3VB3tmd/zmzZvH@0vOdzuMe5fp@4Pp5s1mvFmNa7iO@4ejw9Kd3W6/Z9n22@f89NPBzeToerz9uinPfv4Ym4tXX05TdhC7uIrXcZ0sOjxnume69wulO5hOhq8T7PcTM8XZVeqOqq4/r9PhKHbp5RRP6uN6FlcUrlN8Uc@SmordVUr1/TVddSsfD0e9@ng9Sx52Ei2UTGKnUjLrRvcR@H5gppPRy1kSwV0uCwGdpbCmY7rdCOi5D2@QC@gQMYCTE3hbFOrOwLxgeAOFRGFAIthc0GflbAzEElByoX1UMmslLkEtQokVxg4aTIAFtUmiJqM/TUh98hA97M6R8BoAqQK@0jUwzICrzCO2J3fS5sBWq0LyUBnYfFQlARIDw6VALsJ/VhSCq7J02JbGILQmspmwgocuDbzWbUp3TQVXaLUqgGk/As9R3jqa2@bMgkA2J920KGQIMgoqQcxo5EwupWUFZMwyUHQKDh8Lf@Pid4EoCjOAC4b1Xy4DAePm10QQrNqfAyppRNxMmTvTsDa07Rrmfi9EPnNcZJA5HbTTDE0pjfHgC63KsByjnCatCJfROFz4MQbw7g@NnszPvBX2VwRrHM/b3nFrp71@j5o2chnfgJZltWulbvyhNCmGe8Vok2UQgxlhBlG0/hdP8GdP/I@eOH/2xLMnnnjiw@AX "C (gcc) – Try It Online")
-13 bytes thanks to @ceilingcat using the L-string trick. -5 bytes by moving the remaining local variables to the global scope; it doesn't matter if they are `int` or `char` because the ultimate destination is `s[n]^=u&v`, i.e. assignment to `char`.
A small improvement over [Mitchell Spector's C answer](https://codegolf.stackexchange.com/a/200773/78410). Instead of keeping track of which row and column have the typo, this one computes for *every char* the XOR-sums of its own row and column. If both are nonzero (they are necessarily equal), correct the current cell (which is the only offending cell) right away.
### Commented
```
// declare global(?) variables n,u,v,x,y
// and initialize *R to point to the XOR hash array
n,*R=L"[z@z4zp4y j,-#3#mu7qĘddė|h0\"k5&j4Đzm7ſ4sM6~0=a",u,v,x,y;
// the function: takes char array and modifies it in place
f(char*s){
  // loop over first 529 chars backwards...
  for(n=529;n--;s[n]^=u&v)
    // loop over the chars on the same row/col as n,
    // initialize u=row hash, v=col hash
    for(y=23,u=R[n/y+y],v=R[x=n%y];y--;
      // collect XOR sums of row/col
      u^=s[n-x+y])v^=s[23*y+x];
    // at this point, go back to s[n]^=u&v
    // i.e. fix the typo if both sums are nonzero
}
```
[Answer]
# [PHP](https://php.net/), 91 bytes
```
for(;($a=$argn)[$i];$i++){for($j=9;++$j<123;crc32($a)!=3603504069?:die($a))$a[$i]=chr($j);}
```
[Try it online!](https://tio.run/##dVHBbtswDP0VDvChRrIga9oCnRv0tAE77AuKHhiJiRTIlEdJC4Jhv96UVuJiLbCLIZOPj@89Dm44PTwO@t1GuequGlw3KDtunxr/3DV@Nmv/jJ1mv77vZrNm//DletUZMatrxbaf1qu75ep2ebO8u3/8aj2NxbbBcXpt3DjXdn9Ppx8MnpWnx@wjQ3YU5QiFLZhoPe@mysFnBzgMwZuKTDqmkH4omQSS8cSGAHUuUyBt9IUv0DmQSBSwlMnULVjpRS6/2jsjTOQsMQAKKY1x7H8VSioBMxDjJhAIBV8fVh@/SZXFLVi/8xkDWMwIUatq4A34TgsYh8wU0gJ@Ih//06wCUtnsVSDkONWBo080P7t0JZ1VJ@jxCBvSQFS8LYYs2CI1O0FOvU9pJN9K7Mc0IcUimpXyotoxNNpYwLcPGf3jH0OIh6mltKkYd9k9h4PzanLK7y3Tc1xpXKDHylIuUW@rhCiaGE@J6SX7GgYmSouXONQDnz5/fwU "PHP – Try It Online")
Brute force version, actually checking with replacing each letter with all ASCII between codes 10 and 122 (many useless checks, but it saves bytes for example initializing `$j` to 9 instead of 64, those results can't be positive anyway)
As noted by @LuisMendo and @PeterCordes (thanks), this works because `crc32` [can reliably detect](https://barrgroup.com/embedded-systems/how-to/crc-math-theory) every burst of up to 32 bit errors, and in the ASCII-encoded input, replacing a character will cause a burst of 14 (7\*2) bit errors at most.
[Answer]
# [APL (Dyalog Unicode)](https://www.dyalog.com/), 94 bytes
```
g←252|1⊥⊢⍪⎕AV⍳⊣
252|⊢+0,⍨∘(,'⊂⍷≠Eæ×(í/7Ãs·¨\=Ð¨⍎ëáØ@'∘g∘.⌊⍨'#⌿O⍒`=~{└⊤Üã/cëÖ¶⌹⌹bð'g⍉)23 23⍴-
```
[Try it online!](https://tio.run/##7VVBaxNBFMZr/oQPPGzFNLVJxVPBEgMGLEXTCooHJ7uzuyO7M3Fm1jQqHiwUE7KhSqs9KFirEhRRUCyehOw/mT8S326SWsXePElgd5mZ97033/vmY5Y0glmnRQLhDU380vR2apXCWnn5olUX64x7ILhlOvuUk3pAa0uXV3GCoPJ1S5FAW0PPbG4VzxUfzJvOW9N5beL3GF26ZuIvCMylEVw9czZv4r55vDuTx2KPTHxg2q8qybvk@Uzyce58sqEGB4P@zcVka9A3cS/5kOwluxcsTPDwLZhuB9OtU6b7Y8XET28tnnx432xvm86bE8mLZH/OxoRng2@m@x2fevLZ8kzcPl0sQbFk4q@zQxc5YlkkVl0xnY3Bp5LZfJJ2erWM39VL1dqQzaeY3s5auWZVOTDuChkSzQQH7VMhW0C4A7ZwUknGK02mfSCNRsDsDKkwDSFhI9JUgrIZ5TbN8jQNKAbCiI@heaBSCgkO1dTOdhmVl3I8xdgIYQuupQiASIplbJ@zOxFVSIFoGB0KSBqwbODg4C5FZsIFh3lMkwAcogkIXIWIHwJ/4wK2TzingSrAMuGtY4IZARXVbyNB0GKyDlwwRfOjLv1IjVgrCEkL6hQFQfJOZFMHnEhm2knCVciUSou7UoSpmqBEJFErrEuwHZumbRSg8odGR/onQSCakxCWVZHtj/fOQ9Nn2OREv0NNR3KpdAM8LC2jsdRuRkFIVIxPFMOTDDMxiKKqYOVyrPiPDHJvapD/0SB4gbT3XGBFtErpl1VuTO@SqVWOsUoJrbIw/e1MrfJ3q1w5YpWFnw "APL (Dyalog Unicode) – Try It Online")
Uses the vector of Unicode codepoints as the I/O format. Otherwise it would cost me 11 bytes to convert from/to strings:
# [APL (Dyalog Unicode)](https://www.dyalog.com/), 105 bytes
```
g←252|1⊥⊢⍪⎕AV⍳⊣
⎕UCS 252|46,⍨∘(,-+'⊂⍷≠Eæ×(í/7Ãs·¨\=Ð¨⍎ëáØ@'∘g∘.⌊⍨'#⌿O⍒`=~{└⊤Üã/cëÖ¶⌹⌹bð'g⍉)23 23⍴-∘⎕UCS
```
[Try it online!](https://tio.run/##7VVBaxNBGMVr/oQfeNiKSUqTqKeCJQYMWIqmFRQPTnYn2ZHdmTgzaxoVDxaKWbKlStUeFKxVCYooKBZPQvafzB@J3@4mpRZ68iaBzTIz35s373vz2JCOV3B6xBPtsYnemK0XjVpxrbp82WqKdcbbILhlwn3KSdOjjaWrqzhBUPWmpYinrXHbbG6XzpceLZjwgwnfmegTVpdumOg7AnM4Xqs2IAFULuRNNDRPd@fyhXNI@cREB6b/thZ/jF/NxV/mL8YbanQwGt5ejLdHQxNtxZ/jvXj3koVb2vgrmkGIBNYZM/i9YqLndxZPP35odnZM@P5U/Dren7dxw8vRTzP4hU8z/ma1TdQ/WypDqWyiHwWkyNSMWygZ@XFWXzHhxuhr2Ww@Sxq/XsX36pV6Y8wWEGPVOTDeEtInmgkO2qVC9oBwB2zhJN5MVrpMu0A6HY/ZKVLhNoT4nUBTCcpmlNs03aepR7HgB3wCzQOVUkhwqKZ2ekpGL@VkirUMYQuupfCASIo0tsvZvYAqlEA0ZLcDknosHTg4uE9RmWiBw9pMEw8cogkIXIWAHwL/0gK2SzinnirCMuG9E4qpABU076JA0GK6DlwwRfNZl26gMtUKfNKDJkVDULwT2NQBJ5Cpd5Jw5TOlEvKWFH7iJigRSPQKeQm2Y9OkjSLUjnl0pH/ieaI7LSGtCmx3cnYeui7DJqf@HXqa2aWSA/CytAwmVrdSCUKiY3zqGN6kn5pBFFVFK5djpX@OxoNZNP7HaOBHo7/XAlbCkJSTkNyafT9mITkhJGUMSWX2JzMLyfGQXDsSksof "APL (Dyalog Unicode) – Try It Online")
Uses the row/col checksum approach as in [Mitchell Spector's C answer](https://codegolf.stackexchange.com/a/200773/78410). Because bitwise XOR is not a built-in function in APL, I had to use plain sum and negation instead.
### How it works
```
g←252|1⊥⊢⍪⎕AV⍳⊣  ⍝ Auxiliary function: checksums g matrix → checksum vector
          ⎕AV⍳⊣  ⍝ Convert string written in APL SBCS to integer
        ⊢⍪       ⍝ Append the checksum vector at the bottom of the matrix
      1⊥         ⍝ Column-wise sum
  252|           ⍝ Modulo 252
⍝ Main function: Correct the typo based on checksum
252|⊢+0,⍨∘(,'⊂⍷≠Eæ×(í/7Ãs·¨\=Ð¨⍎ëáØ@'∘g∘.⌊⍨'#⌿O⍒`=~{└⊤Üã/cëÖ¶⌹⌹bð'g⍉)23 23⍴-
23 23⍴-  ⍝ Negate each element and reshape into 23x23 matrix, discarding the last '.'
(...)    ⍝ Pass to the inner tacit function...
'...'g⍉    ⍝ Check rows against the row checksums
∘.⌊⍨       ⍝ Outer product by minimum (rows and columns reversed) with...
'...'∘g    ⍝ Check columns against the column checksums
           ⍝ Result = all-zero matrix except at the typo (difference mod 252)
,          ⍝ Flatten the result
0,⍨∘     ⍝ Append a zero to account for the last '.'
252|⊢+   ⍝ Fix the typo using the offset
```
[Answer]
# Java 10, ~~140~~ ~~139~~ 137 bytes
```
s->{var r=s;for(int i=0;;i++)for(var c='z';c>65;c-=c==97?7:1)if((r=s.substring(0,i)+c+s.substring(i+1)).hashCode()==0xA89D0862)return r;}
```
-2 bytes thanks to *@ceilingcat*.
[Try it online.](https://tio.run/##7VVNbxMxEL33V4xy6a6SbFMk@sHKRQg49EBViVsRB8eedB28duKPhFDlt6dj76YEJJCQOEEvK@/MeOa9N0@7c77i47n8shOaew8fuDIPRwDKBHQzLhBu0ivAx@CUuQdR9Adf1hTfHtHDBx6UgBswwGDnx1cPK@7AMV/PrCuoEyg2qWs1HJYpkJKCHX87rsXV2ctajJlg7PL89fmr01LNioIuVj5OfZ5TTEaqHIrhYUgNT8uyarhv3lqJRcnY5Oubi8t3k4uzF6XDEJ0BV293dQK3iFNN4HqMK6sktMSxp/HpM/CyIwgnJ3DNlsQcNHKZOA6uzaAjv/EB28rGUC3oWtCmMJUoBktD5cSppebWQGjQug1wI0HY3KGPrFVogC8WhCRX@jRF2HYRSWXwQqEhpdO9gBop0UbTl44AnbMOJAYUeUrX3rn@lXJdhbAmOKuBO6Q2ojFqGdETBB4ADZ9qBIda5YOkwwoJmZ2BVPcqcA2SBw6WohDNU@EPWEA03BjUviKfmM0vkhkAbWtOACHYfRyMVR5HHcsm@g61p21sYIrJcM7KKFCCjNlhwXHjW@V9aj5ztk1qgrfRkVbUlxMdgYlGBe9/0uiAP9farvepZNwomn72CNaNIpJ7/Z407eTyaQAtK7jYSz3LEKwjxcxeMdpkm8XgHn01KMtsu2wnzu5SenC4@d8b6vpPDHX3bKj/zFCC3aY0EVE6f6ByzV@01PM36p@21O13S6V/93b3CA)
**Explanation:**
Also uses an hash like most other answers. The hash of the expected text is `-1466103710`. All other possible double replacements of letters cause no collisions, which has been verified locally with [this test program](https://tio.run/##jVVbb9owFH7vrzjNw5QoNKOvZWzquu5@b3fXNBnbEKeOTX2BsYnfzo4TEggFaUgojs/t@z6f4xRkRk4KdrNaUUmshTdEqL9HAFM/koKCdcThY6YFgxJN8ZUzQk1@/ASSBDcAqdUkuBkHQ7haWMfLTBGlr0XJ42RQ@dRBwH9POXWcoSP50f9Z24RykF9uLI1TlhObX2i2SVLn1t5lU0znpIqjNq5xPoMobbMlmwpUexUA9juAJHeOG4v7ERlRxseTXBQ3slR6emus87P578Wf88cXTy6fPnv@4uWr12/evnv/4ePV9afPX75@@x7VycbaxKGGGPYHIB60BCRXE5cjfBBpulYL0Yzj43XdjGrlUFQbtzE0J@bcxSJJoyhJ1iEAwVEozwfrnVAyuAKFs4ZF5vRFiDaGLOKkrdeSNXwqCeXsnaJ8W2frR7byiPs9rEvTPRaRniaDNmFDuAiEi32Eiy3CB0lvA2qIFzvE95HvCMD@Q4A9IlzPRaVCB8OOEiw9YO2q0fDrpM74rSdyc7BdRvi7d6/rv@l2GA43A7HDYu8UpGnV3WkUmp@mURylIo2SXoQMwkuBL8HUKbhDYHm0b92s6ufy0BhW5R@ePtrluHWCFT58Sims0MriAXrFjqNDIWcHc0Vv9Z08g2r@TyjqB8KCV@LW8yw6fHHEdy6qk@oKS@6f/ur3@80fIVuO7cdsnWt5tFytVi8UXinYgCVejVqBy7k2CyCKIS4Wmmy9MxcuBzKd4j1aeVoMQ5dy6rFbwVLBwySGOMcllilLBF679oAbo7G7ucM2CFXq9MasX9FWe4ThMFoCMRzT0LyibhECccAVGUmOPS5FtWC4mHFEpsfAxEQ4IoERR0DjLqrWOnawAM6ZUlzaDD8OanHAWAHAESkQIDjd7IPSwvJezTL3tkZt8VuygBEPV7PRzGNTAvPVgDpDlC2FDacLY6PLoCZY7Q1qhXkJ0qE80MjgckejLf5ESj1vTOED5Wm@rt2DeS6QZKNfq2ktlw0F8LCc8WupxxUEbVAx1SiGJ1lWYhDLbfYP). (Obviously times out on TIO since it has to do \$(52\times 530)^2=759\text{,}553\text{,}600\$ iterations, including calculating the hash-code and substituting characters in each iteration. It takes between 8 and 9 minutes locally.)
```
s->{                    // Method with String as both parameter and return-type
  var r=s;              //  Result-String (starting at the input to save bytes)
  for(int i=0;;i++)     //  Loop `i` upwards from 0 indefintely:
    for(var c='z';c>65; //   Inner loop over the characters `c` in the ASCII range ['z','A']
        c-=c==97?7:1)   //   skipping the characters "`_^]\[" between 'a' and 'Z':
      if((r=s           //    Set `r` to the input-String,
             .substring(0,i)+c+s.substring(i+1))
                        //    with the `i`'th character replaced with `c`
         .hashCode()==0xA89D0862)
                        //    And if its hash-code is -1466103710:
        return r;}      //     Return `r` as result
```
[Answer]
# C (gcc), 125 bytes
Note: my code includes 2 unprintable characters `\x0c` and `\0x01` in the string literal which aren't visible in the snippet.
```
b,i,j,m,n;C(char*T){b=i=n=0;for(;i<10;b|=("N<Syr4Zd"[i]!=m)<<i++)for(m=j=0;T[j];n^=!i*T[j++])m^=(1&j>>i)*T[j];T[b]^=120^n;}
```
[Try it online!](https://tio.run/##dVRNb9NAEBXHWPyIIQiwG5M2iAPCcSVUOCABF3qipGi9O6k3Xe@G/WgbQX97mV3HaVoJKVKcNzNv3rwZh7@@4PzurilluSq7UlcnOW@ZPTgt/jS1rHV9VC2NzSs5nx1Vzd86H3@bf3@6sW9/iCfjM7l4VnfFfC4nkyKmdfWKCk7PVotKn9fP5AE9TiaLojuv89nL1fGxLA5S9PSsWZzXszdH57q6vcuy51JzFQTOnRdKNtP2eB@yUl88goQ0EcmiWDjweOOhhvFnDVKTkI55aTT4Fo3dANMCuBFEMiDX0rfA1mslecp0VEYp3Tp4tOC4RM0x1XlUSIEu6G1qCWitsSDQI09denprtz8p1mdwo701CphFouGtlr8DOpLAPKBmjUKwqGR6EPRwhaTMLEHIC@mZAsE8A0MoBL1LfKAFaHqtUbkpfGV6859gEuBCsyKB4M2AgzbSYdlP2QbXq3bQsQ00SIaQeBE4ChDBJu8s066TzkXypTVddBOcCZa8Il5G43CMY0zh0yOP9uZnSpnrIUS0LvB227uE61bSkIN/O097u1xsQMvyNmytXiYJxpJjenCMNtklM5hDNx1XWUajECR1XmR/slF/MQ0Tv7ZX00VFPCdahTqPYDGZFVU2IoSvN/mQWkKKUSAb0Y1BHnljUGqBN0R0VKWMsx22qO7j9IYAdX9YecVUQKp8PXvzrtpD5vAQGIpHh4fw0aDTrzxcG3vZH7IOSsWtWsbpfB1tj7PgMHqzIUW2I3M89tvq36Ut1ZfAL6XagPRgNH1TfSAzNtAyFzeaWkTBTK1b1qCXfK9PYpFLyPcmIROKdPhSByTrKWOwb88Wmjj9yxS7yipmnuysLqqBO@6gW@e9/7vw1o3Rmqbxy3zcn5s2fjgaFO/hhUgf91OPy/s9lPeu7hGmfiOLPlgNs/TrdkR7Ht1m9Ml2jT6Q0x6dd/FsOaJAMR1T9e3dPw "C (gcc) – Try It Online")
The `i`th character in the literal is the XOR of all the characters in the original text at positions whose `i`th bit is `1`. The program performs the same XOR on the modified text. The computed value will be different iff the modified character is in a position with `i`th bit `1`. This gives us all the bits of the modified character's position.
Finally, we modify the character such that the XOR of all characters is that of the original text(120).
[Answer]
# [Charcoal](https://github.com/somebody1234/Charcoal), ~~52~~ 49 bytes
```
θＪ↨²⮌Ｅ =&%ce9KG*¬⁼§γΣＥΦθ＆μＸ²κ⌕γλι⁰§γ⁻²⁸ΣＥθ∧⁻κⅈ⌕γι
```
[Try it online!](https://tio.run/##dZJNb9swDIbv/RVEgQ5yoQVDTxuKHVqgHbohQ7Ht0CsjMbFWmXL0US@/3qPluMsG7GSKpF6@fGTTYjQB/Tg@RsdZ7Zvrs8@l638EdYuJ1JWGb/RCUcI19uocPr65MPThy6fLcw1fQ1Z3@4I@qZv8wJZ@qZ2G76WrvffOZ4pqr@HW5cElumGrOg2PYZC0CD83TaPh3klarvmmHl0zf9@JkdnSifTacUnq6v2fIaI@yc6FZw1P6lRzFmuux/GBwfE2xA6zCwy5pRAPgGzBBOt4t2QGl1vAvvfO1M4k16Sl64vsAsk4YkP1XiZPUugKH1s1UIwhgqVMpk6Z5WM8HqU2d5jAOQYPGElkTMtuXyiJBcxAjBtPEMm7GlgJhP8Bwhas27mMHixmhCBZKPza@JcXMC0yk08rWCMf/lOsBlLZ/BSDkMOSBw7yWnresi1pdp2gwwNsSICIeVsMWbAlVnYROXUupUl8G0M30YQUShRWoouyjqFpjRXc/cPoZH/0PgxLSWRTMe1xtoahdbLkwu@V6YwrTQPksXIsR9TbaiFEIcYLMXnJrsKQHzutzsa3L/43 "Charcoal – Try It Online") Link is to verbose version of code. Works with all printable ASCII, not just letters. I originally wanted to do a Hamming check on all of the letters in the input but I was too ill to implement that at the time and by the time I got better I realised that @GB's modular sums would suit Charcoal better anyway. Explanation:
```
θ
```
Print the input string.
```
Ｅ =&%ce9KG*
```
Loop over 10 partial sums. These characters are the result of cyclically indexing the appropriate sum from the correct input back into the predefined printable ASCII variable.
```
ΣＥΦθ＆μＸ²κ⌕γλ
```
Filter on the desired characters for this sum and take the sum of their indices within the predefined printable ASCII variable.
```
Ｊ↨²⮌...¬⁼§γ...ι⁰
```
Interpret those sums that don't match as a number in base 2 LSB first, and jump to that character, which will be the incorrect character.
```
§γ⁻²⁸ΣＥθ∧⁻κⅈ⌕γι
```
Subtract the sum of all the other printable character indices from 28 which gives the index (modulo 95) of the current character.
@xnor's algorithm can be implemented in 42 bytes:
```
θ≔⁻⁵⁰⁹⁹⁸ΣＥθ℅ιηＪ÷⁻¹³⁴⁴⁷⁴²⁰ΣＥθ×℅ικη⁰§γ⁺η⌕γＫＫ
```
[Try it online!](https://tio.run/##dZLbbhMxEIbv@xRz6UgmSiEIql5VKkhBiqhEX8CxJ/FQe7zxISFPv0z2UCgSe2XP4Z9/Pq/1JttkQt8/ZeKqjov7m4dS6MBqS9yK@ri6u/us4UeLams6ddTwPTtiExQt5NPgpeNbi91zUhuuj3Qih1Pv7Yf1@tP6/epN@zNFLOqPiIaXWUjDSsRGIw91ww5/qYOGpyBaXsNXYjfcEV/UtWdx3/cbBuJ9ytFUSgzVY8oXMOzAJhlxmCNnqh5M1wWyQ2WRNimJXauYoVhCtjj0VQwoidh4KtWAOacMDivaYcoon/N0ldxYYRPXnAKYjCJjPdOxYRELpgKy2QWEjIGGg5PDCcVZ2oOjA1UTwJlqIEkUGr8WvvEC1htmDGUJW8OX/yQHA6XtfopBqGmOAycqqMctfSuj6wLRXGCHAkTMu2bRgWt5YJcNl0jyP4j4Pqd4pQkltSysRNfIOhavayzhyz@M/trfhJDOc0pkS7N@mq3h7EmWnPm9Mh1xlesAeaya24R6P1hIWYjxTExeMg4wTMGyvOnfncJv "Charcoal – Try It Online") Link is to verbose version of code. Works with all printable ASCII, not just letters, but fails if the input is correct (would cost 3 bytes to fix). Explanation:
```
θ
```
Print the input string.
```
≔⁻⁵⁰⁹⁹⁸ΣＥθ℅ιη
```
Calculate the size of the error.
```
Ｊ÷⁻¹³⁴⁴⁷⁴²⁰ΣＥθ×℅ικη⁰
```
Calculate and jump to the position of the error.
```
§γ⁺η⌕γＫＫ
```
Correct the character that is in error.
[Answer]
# [Python 3](https://docs.python.org/3/), 364 bytes
Output is bz2 encoded. Input is ignored. Code contains unprintable characters, so here is the hex version. Utf-8 encoded version is 55 bytes regular code + 309 bytes compressed data. See commented lines in the header of the TIO link. 
```
import bz2
lambda s:bz2.decompress(b'BZh91AY&SY\xd3\xaa\xfe\xfd\x00\x00(\x15\x80@\x05\x02"?\xf7\xff\xa00\x01F\n\x86\x82bI\xfa\x14\xf4&\x99\r\n\x00\x06\x80\x00\x04\xa6\x84\x9a\x9e\xd455<\xa6\x99\x19\x10o\xcf|b5oN"\x06V\xab\xd0\xa3\x8awA\xbb}\x8a=\xa6\xcc\x9b#\x1a\xcd\x94Q\x83%Lp\xc9-n\x99\xd9\xee\xb5\xbc\xb4xc\x1a\xeaz\x03\xe49*\x90\xc9E\xf8\xa1y\xb7q\xcb!O\xbeA\xf5\rj\xd9Vg2\xec\xe9&A\x08\xb0\x91\x0c\xde\x05_m\xeb.\xa8\x9a~\x0b?\xec\x0e+\x14\xf0q\x05\x19\xa3O\x84\xb0\xbb\xea\xb6\xc3d\xab\xa6\xa3\x1b\x02c\xd3\x8fE\x99F\xf8\xdf\x07\x95\xdam\x10\x1b!\xb0m\xd7m\xb9\xdf\x9d:&\x0e\x13\xa4{S\xa3\x9ao\x95\x98\r\x995#\xdb\xbb\x17\x1d`2\xd0\x1d7$\xb5E\xe1Y9\xf4u\xbd\xcc9\x01va\x0e\x12\x7fA\x06\xa6[\xceg>\x05\xc8T\xaf\x87\x1a\t\x18Hcq\x87\x04\xa2\xa5\xf8\xc2\x0643\xbci\x14\x14\xbe5\xd77\x99w\'\xba&\r\x19b\x1ck\x16\x10&v\xd0\xb9\xe1W\xc3\x82{\xbe\xdb-\x81\xb0\xac\xc8A\xfc]\xc9\x14\xe1BCN\xab\xfb\xf4').decode('ascii')
```
[Try it online!](https://tio.run/##dVRrb9s2FP2eX8Gmmx1vqSH5EUvFsi4dUqzA1qJo0SGbho2Pq1idRDqkFMvt1r@enivaWTdgH0iR93HuuefS3uzatbPzu3B@/NyKypbON7KtnBXtmpzfCWmN0M5U9vpg2VbtWsjNpq70EBmQhpBm07XkRdAVWU1DXks1wdF0dh96Ksh754WhlvRQJcJ7v7/CFyO0s613tZCeAKPXtrrpKICCbAVZqWoSnupqOBgcbgnMXClMdV21shZGtlI4WEVn7wP/xUXotbSW6jAVP0m7@x/nQCB06h0IitYd7MK6KtBp7HLdhcg6iEbuhCIIAvKm02SE6fygnZc2NFUIDF5617CaIrjOQyvgSrSjiduYisv/aPRZ/7Ku3fbgAmzo9Hpf@1Rs1xWaPOh3r2mUK3ABDKv13V7qcqDgPBSzB8UwyWYQQwYK0@Ojqtk43wr1fnakz7FPec6eQjgJU4zZGToZd22ZjSeTo4dig1bbE/3PsSaL6@TuM5haNspIER4zmqF7PDV@@ss6Ty@uRq@vit7Mi17Koi8JyxR9kvA6Kfp0WfRZ8h1uOCSz4yfwr7BKxA9B6bPCIuQMa6aewwOUdIHvYlT0eV54dg@RHJLszwiQfMc3R0KOsmaxXH4TzUjrU16JK3pd/qWW7sUxI7yFXyEUEBKMM7m9KHql/ubjeczVGvnqIZKBq9FKvngF9/zLHze4549shDdYhKoKbSmkqEWvYw7J9ygFdFrkXyE44bRLNJShQLpD6OoGJvXgJY6E@uWy8O8Y8e31DFmAoXwEe4IEhew8xRlWQ6zi7w38agqsjHv/CJt6EtMS@nqvXXITBWcR5PxlFIqxlGKC@HKncxPl4LZZjlTxiHScZlZecqfPInGDeSWYWw5QIxuWluMfMCpuZoVN5TEuN49HTAYB/CoWH15H@Fy6CJBnmCqwl1DZqEgqBXhq/pjF4aRm9QVrCwqUXuX8GjrcDY8n50dzK/cVkLAqL@LrkGe/IoCuv43N6@wNbCCUrYbJtNizH/RNNAxPCNlyGTvUMwZZzHmcVZSRlyLueMWt59tiDIMcMfs0Z9L6T2xnrMboNjJnESj9mdXlF/2BEbjLR7ilcQZSMzcevP6N30YsROnT71/EeZS8FuPJ8HPjX6zEn3Q1ntzdfQI "Python 3 – Try It Online")
]