(I just work up and my brain is not yet fully functional, so what follows is probably totally stupid)
Notation: "||" means string concatenation.
Let password P = P1 || P2, where len(P1) + len(P2) = len(P) and |len(P1) - len(P2)| <= 1.
Let H1 = hash(P1), H2 = hash(P2), where hash() is a cryptographic hash function that produces at least as many bits as the longest allowed password and satisfies whatever slowness and memory use requirements that you have for a password hash.
To store a password, store P ⊕ H1 and P ⊕ H2.
To check a password candidate C received at login, let C = C1 || C2 using the same splitting rule as used for P above, and compute C ⊕ hash(C1) and C ⊕ hash(C2).
A login is successful if either P ⊕ H1 or P ⊕ H2 is within edit distance 1 of either C ⊕ hash(C1) or C ⊕ hash(C2).
(I've omitted salt from the above for simplicity. Replace the hash with a salted hash if you want salt).
Notation: "||" means string concatenation.
Let password P = P1 || P2, where len(P1) + len(P2) = len(P) and |len(P1) - len(P2)| <= 1.
Let H1 = hash(P1), H2 = hash(P2), where hash() is a cryptographic hash function that produces at least as many bits as the longest allowed password and satisfies whatever slowness and memory use requirements that you have for a password hash.
To store a password, store P ⊕ H1 and P ⊕ H2.
To check a password candidate C received at login, let C = C1 || C2 using the same splitting rule as used for P above, and compute C ⊕ hash(C1) and C ⊕ hash(C2).
A login is successful if either P ⊕ H1 or P ⊕ H2 is within edit distance 1 of either C ⊕ hash(C1) or C ⊕ hash(C2).
(I've omitted salt from the above for simplicity. Replace the hash with a salted hash if you want salt).