|
| 1 | +// One Away: There are three types of edits that can be performed on strings: insert a character, remove a character, |
| 2 | +// or replace a character. Given two strings, write a function to check if they are one edit (or zero edits) away. |
| 3 | + |
| 4 | +const assert = require("assert"); |
| 5 | + |
| 6 | +/** |
| 7 | + * Check if the two strings are zero or one edits away |
| 8 | + * @param {string} A input string one to check against |
| 9 | + * @param {string} B input string two to check against |
| 10 | + * @return {boolean} whether the input strings can be edited zero or one ways to be equal |
| 11 | + * |
| 12 | + * There are two cases here: (1) A.length === B.length, and (2) A.length !== B.length. |
| 13 | + * For the first case, all we need to do is iterate through the strings and ensure their elements are equal, allowing |
| 14 | + * room for one misplaced character. Iterating through this takes O(N) time, without any additional space. |
| 15 | + * For the second case, we iterate through the different length strings, and perform almost identical comparisons. |
| 16 | + * Again, we allow a single misplaced character, or lack of character as we iterate through them. This also takes O(N) |
| 17 | + * time. For the sake of keeping things DRY, we find out which string is the shorter one and instantiate a copy of it, |
| 18 | + * as well as a copy of the long string. For this reason, we take additional O(N) space. If we wanted, we could perform |
| 19 | + * two while loops (a < A.length + 1 && b < B.length) as well as (a < A.length && b < B.length + 1), so we don't take |
| 20 | + * any additional space. |
| 21 | + * Runtime: O(N) |
| 22 | + * Space: O(N) |
| 23 | + * |
| 24 | + */ |
| 25 | +const oneAway = (A, B) => { |
| 26 | + if (Math.abs(A.length - B.length) > 1) return false; |
| 27 | + |
| 28 | + if (A.length === B.length) { |
| 29 | + let slack = true; |
| 30 | + for (let i = 0; i < A.length; i++) { |
| 31 | + if (A[i] === B[i]) continue; |
| 32 | + |
| 33 | + if (!slack) return false; |
| 34 | + slack = !slack; |
| 35 | + } |
| 36 | + return true; |
| 37 | + } |
| 38 | + |
| 39 | + const shortStr = A.length < B.length ? A : B; |
| 40 | + const longStr = A.length < B.length ? B : A; |
| 41 | + let sIdx = 0; |
| 42 | + let lIdx = 0; |
| 43 | + let slack = true; |
| 44 | + while (sIdx < shortStr.length + 1 && lIdx < longStr.length) { |
| 45 | + if (shortStr[sIdx] === longStr[lIdx]) { |
| 46 | + sIdx += 1; |
| 47 | + lIdx += 1; |
| 48 | + } else { |
| 49 | + if (!slack) return false; |
| 50 | + slack = !slack; |
| 51 | + lIdx += 1; |
| 52 | + } |
| 53 | + } |
| 54 | + return true; |
| 55 | +}; |
| 56 | + |
| 57 | +describe(module.filename, () => { |
| 58 | + it("should return true when input strings are equal.", () => { |
| 59 | + assert.ok(oneAway("tech", "tech")); |
| 60 | + }); |
| 61 | + it("should return true when input strings are of equal length, and replacing a character makes them equal.", () => { |
| 62 | + // replacing at the start of the string |
| 63 | + assert.ok(oneAway("sech", "tech")); |
| 64 | + assert.ok(oneAway("tech", "sech")); |
| 65 | + // replacing in the middle of the string |
| 66 | + assert.ok(oneAway("tdch", "tech")); |
| 67 | + assert.ok(oneAway("tech", "tdch")); |
| 68 | + // replacing at the end of the string |
| 69 | + assert.ok(oneAway("tecg", "tech")); |
| 70 | + assert.ok(oneAway("tech", "tecg")); |
| 71 | + }); |
| 72 | + it("should return true when input strings lengths are one apart and inserting/deleting a character makes them equal.", () => { |
| 73 | + assert.ok(oneAway("tech", "stech")); |
| 74 | + assert.ok(oneAway("tech", "ech")); |
| 75 | + }); |
| 76 | + it("should return false when the input strings are not zero/one edits away.", () => { |
| 77 | + assert.ok(!oneAway("tech", "stack")); |
| 78 | + assert.ok(!oneAway("tech", "techqueria")); |
| 79 | + }); |
| 80 | +}); |
0 commit comments