Let's do some “enciph5r47g”

Retina, 24 23 bytes

(.)(?<=\1(.{0,9}).)
$.2

Try it online!

A fairly simple regex substitution. We match each character and try to find a copy of it 0-9 characters before it. If we find it, we replace the character with the number of characters we had to match to get to the copy.

The results don't quite match the test cases, because this one uses the largest possible digit instead of the smallest possible one.


JavaScript (ES6), 74 57 54 bytes

Saved 3 bytes thanks to ETHproductions with the brilliant p=/./g instead of p={} (inspired by Neil)

s=>s.replace(p=/./g,(c,i)=>(i=p[c]-(p[c]=i))>-11?~i:c)

Test cases

let f =

s=>s.replace(p=/./g,(c,i)=>(i=p[c]-(p[c]=i))>-11?~i:c)

console.log(f("abcd"));
console.log(f("aaaa"));
console.log(f("banana"));
console.log(f("Hello World!"));
console.log(f("this is a test"));
console.log(f("golfing is good for you"));
console.log(f("Programming Puzzles & Code Golf"));
console.log(f("Replicants are like any other machine. They're either a benefit or a hazard."));


Haskell, 72 66 bytes

Thanks to Laikoni for golfing 6 bytes!

(a:r)%s=last(a:[n|(n,b)<-zip['0'..'9']s,b==a]):r%(a:s)
e%s=e
(%"")

Try it online!

The function % keeps the partially processed string in reverse in its second argument, so it's able to search the first 10 elements of this string for occurences of the character it's examinating. The submission consists of the unnamed function (%"") which calls the previous function with the empty string as its second argument.