There is a strong connection with rainbow tables. A common misconception is that rainbow tables are a complete set of hash values. What they actually are a method of saving storage space at the cost of making searches slower.
The idea is to make a function that maps hash values back to password-like strings. With that function chains like this are built:
I=initial password guess
P=new password-like string
V=hash value
H()=hash function
U()=unhash function (converts V to a new P)
I -> V0=H(I0) -> P0=U(V0) -> V1=H(P0) -> P1=U(V1) -> ... -> Vfinal
Make many chains with different initial guesses, and store the first and last values [I, Vfinal] in a database. When you find a password hash, you repeat the H(U(H(U(...)))) process until you it matches one of your saved Vfinal values. This tells us the hash value path from the password hash we're trying to break and hash from the known initial guess merged, just like in this card trick.
Re-creating the chain from the chain of hashes from our known initial value, we discover a string that might not be the actual password, but is a string that hashes to the same value. With modern cryptographic hashes, collisions are unlikely, but rainbow tables were invented when passwords used very poor hashes.
Rainbow tables are actually a slight revision on this, because the merging of paths is a bad thing as it leads to false positives. Specifically, I can detect that my hash eventually leads to the end of a chain, but it may very well be that it merged into that chain at some point and so not contained in my table.
Rainbow tables minimize the chance of merges by using a sequence of unhash functions in chains so a merge can only occur if it happens the same point in the sequence of unhash functions.