From a410a723b802d2791ea62e35814ebd0343e3453d Mon Sep 17 00:00:00 2001 From: Rob Simpson Date: Sun, 15 Oct 2023 13:57:40 -0400 Subject: [PATCH 1/2] Abbreviation --- Dynamic-Programming/Abbreviation.js | 46 +++++++++++++++++++ .../tests/Abbreviation.test.js | 20 ++++++++ 2 files changed, 66 insertions(+) create mode 100644 Dynamic-Programming/Abbreviation.js create mode 100644 Dynamic-Programming/tests/Abbreviation.test.js diff --git a/Dynamic-Programming/Abbreviation.js b/Dynamic-Programming/Abbreviation.js new file mode 100644 index 0000000000..e77aa96006 --- /dev/null +++ b/Dynamic-Programming/Abbreviation.js @@ -0,0 +1,46 @@ +/** + * @description + * Given two strings, `a` and `b`, determine if it's possible to make `a` equal + * to `b` You can perform the following operations on the string `a`: + * 1. Capitalize zero or more of `a`'s lowercase letters. + * 2. Delete all the remaining lowercase letters in `a`. + * + * ### Algorithm + * The idea is in the problem statement itself: iterate through characters of + * string `a` and `b` (for character indexes `i` and `j` respectively): + * 1. If `a[i]` and `b[j]` are equal, then move to next position + * 2. If `a[i]` is lowercase of `b[j]`, then explore two possibilities: + * a) Capitalize `a[i]` or + * b) Skip `a[i]` + * 3. If the `a[i]` is not uppercase, just discard that character, else return + * `false` + * + * Time Complexity: (O(|a|*|b|)) where `|a|` => length of string `a` + * + * @param {String} a + * @param {String} b + * @returns {Boolean} + * @see https://www.hackerrank.com/challenges/abbr/problem - Related problem on HackerRank. + */ +export const abbreviation = (a, b) => { + const n = a.length + const m = b.length + + let dp = Array.from({length: n + 1}, () => Array(m + 1).fill(false)) + dp[0][0] = true + + for (let i = 0; i < n; i++) { + for (let j = 0; j <= m; j++) { + if (dp[i][j]) { + if (j < m && a[i].toUpperCase() === b[j]) { + dp[i + 1][j + 1] = true + } + if (a[i] === a[i].toLowerCase()) { + dp[i + 1][j] = true + } + } + } + } + + return dp[n][m] +} \ No newline at end of file diff --git a/Dynamic-Programming/tests/Abbreviation.test.js b/Dynamic-Programming/tests/Abbreviation.test.js new file mode 100644 index 0000000000..30830b5cc5 --- /dev/null +++ b/Dynamic-Programming/tests/Abbreviation.test.js @@ -0,0 +1,20 @@ +import { abbreviation } from '../Abbreviation.js' + +describe('Abbreviation', () => { + test('it can abbreviate string a to b', () => { + expect(abbreviation('', '')).toBe(true) + expect(abbreviation('a', '')).toBe(true) + expect(abbreviation('', 'A')).toBe(false) + expect(abbreviation('a', 'A')).toBe(true) + expect(abbreviation('abcDE', 'ABCDE')).toBe(true) + expect(abbreviation('ABcDE', 'ABCDE')).toBe(true) + expect(abbreviation('abcde', 'ABCDE')).toBe(true) + expect(abbreviation('abcde', 'ABC')).toBe(true) + expect(abbreviation('a', 'ABC')).toBe(false) + expect(abbreviation('abcXYdefghijKLmnopqrs', 'XYKL')).toBe(true) + expect(abbreviation('aBcXYdefghijKLmnOpqrs', 'XYKLOP')).toBe(false) + expect(abbreviation('abc123', 'ABC')).toBe(true) + expect(abbreviation('abc123', 'ABC123')).toBe(true) + expect(abbreviation('abc!@#def', 'ABC')).toBe(true) + }) +}) \ No newline at end of file From 6da05a27883f10db27d92a304371a5e2a615879b Mon Sep 17 00:00:00 2001 From: Rob Simpson Date: Thu, 2 Nov 2023 16:26:00 -0400 Subject: [PATCH 2/2] Updates from code review --- Dynamic-Programming/Abbreviation.js | 65 ++++++++++--------- .../tests/Abbreviation.test.js | 46 ++++++++----- 2 files changed, 61 insertions(+), 50 deletions(-) diff --git a/Dynamic-Programming/Abbreviation.js b/Dynamic-Programming/Abbreviation.js index e77aa96006..06092216b8 100644 --- a/Dynamic-Programming/Abbreviation.js +++ b/Dynamic-Programming/Abbreviation.js @@ -1,46 +1,47 @@ /** * @description - * Given two strings, `a` and `b`, determine if it's possible to make `a` equal - * to `b` You can perform the following operations on the string `a`: - * 1. Capitalize zero or more of `a`'s lowercase letters. - * 2. Delete all the remaining lowercase letters in `a`. + * Given two strings, `source` and `target`, determine if it's possible to make `source` equal + * to `target` You can perform the following operations on the string `source`: + * 1. Capitalize zero or more of `source`'s lowercase letters. + * 2. Delete all the remaining lowercase letters in `source`. * - * ### Algorithm - * The idea is in the problem statement itself: iterate through characters of - * string `a` and `b` (for character indexes `i` and `j` respectively): - * 1. If `a[i]` and `b[j]` are equal, then move to next position - * 2. If `a[i]` is lowercase of `b[j]`, then explore two possibilities: - * a) Capitalize `a[i]` or - * b) Skip `a[i]` - * 3. If the `a[i]` is not uppercase, just discard that character, else return - * `false` + * Time Complexity: (O(|source|*|target|)) where `|source|` => length of string `source` * - * Time Complexity: (O(|a|*|b|)) where `|a|` => length of string `a` - * - * @param {String} a - * @param {String} b - * @returns {Boolean} + * @param {String} source - The string to be transformed. + * @param {String} target - The string we want to transform `source` into. + * @returns {Boolean} - Whether the transformation is possible. * @see https://www.hackerrank.com/challenges/abbr/problem - Related problem on HackerRank. */ -export const abbreviation = (a, b) => { - const n = a.length - const m = b.length +export const isAbbreviation = (source, target) => { + const sourceLength = source.length + const targetLength = target.length - let dp = Array.from({length: n + 1}, () => Array(m + 1).fill(false)) - dp[0][0] = true + // Initialize a table to keep track of possible abbreviations + let canAbbreviate = Array.from({ length: sourceLength + 1 }, () => + Array(targetLength + 1).fill(false) + ) + // Empty strings are trivially abbreviatable + canAbbreviate[0][0] = true - for (let i = 0; i < n; i++) { - for (let j = 0; j <= m; j++) { - if (dp[i][j]) { - if (j < m && a[i].toUpperCase() === b[j]) { - dp[i + 1][j + 1] = true + for (let sourceIndex = 0; sourceIndex < sourceLength; sourceIndex++) { + for (let targetIndex = 0; targetIndex <= targetLength; targetIndex++) { + if (canAbbreviate[sourceIndex][targetIndex]) { + // If characters at the current position are equal, move to the next position in both strings. + if ( + targetIndex < targetLength && + source[sourceIndex].toUpperCase() === target[targetIndex] + ) { + canAbbreviate[sourceIndex + 1][targetIndex + 1] = true } - if (a[i] === a[i].toLowerCase()) { - dp[i + 1][j] = true + // If the current character in `source` is lowercase, explore two possibilities: + // a) Capitalize it (which is akin to "using" it in `source` to match `target`), or + // b) Skip it (effectively deleting it from `source`). + if (source[sourceIndex] === source[sourceIndex].toLowerCase()) { + canAbbreviate[sourceIndex + 1][targetIndex] = true } } } } - return dp[n][m] -} \ No newline at end of file + return canAbbreviate[sourceLength][targetLength] +} diff --git a/Dynamic-Programming/tests/Abbreviation.test.js b/Dynamic-Programming/tests/Abbreviation.test.js index 30830b5cc5..86e0f97336 100644 --- a/Dynamic-Programming/tests/Abbreviation.test.js +++ b/Dynamic-Programming/tests/Abbreviation.test.js @@ -1,20 +1,30 @@ -import { abbreviation } from '../Abbreviation.js' +import { isAbbreviation } from '../Abbreviation.js' -describe('Abbreviation', () => { - test('it can abbreviate string a to b', () => { - expect(abbreviation('', '')).toBe(true) - expect(abbreviation('a', '')).toBe(true) - expect(abbreviation('', 'A')).toBe(false) - expect(abbreviation('a', 'A')).toBe(true) - expect(abbreviation('abcDE', 'ABCDE')).toBe(true) - expect(abbreviation('ABcDE', 'ABCDE')).toBe(true) - expect(abbreviation('abcde', 'ABCDE')).toBe(true) - expect(abbreviation('abcde', 'ABC')).toBe(true) - expect(abbreviation('a', 'ABC')).toBe(false) - expect(abbreviation('abcXYdefghijKLmnopqrs', 'XYKL')).toBe(true) - expect(abbreviation('aBcXYdefghijKLmnOpqrs', 'XYKLOP')).toBe(false) - expect(abbreviation('abc123', 'ABC')).toBe(true) - expect(abbreviation('abc123', 'ABC123')).toBe(true) - expect(abbreviation('abc!@#def', 'ABC')).toBe(true) +const expectPositive = (word, abbr) => + expect(isAbbreviation(word, abbr)).toBe(true) +const expectNegative = (word, abbr) => + expect(isAbbreviation(word, abbr)).toBe(false) + +describe('Abbreviation - Positive Tests', () => { + test('it should correctly abbreviate or transform the source string to match the target string', () => { + expectPositive('', '') + expectPositive('a', '') + expectPositive('a', 'A') + expectPositive('abcDE', 'ABCDE') + expectPositive('ABcDE', 'ABCDE') + expectPositive('abcde', 'ABCDE') + expectPositive('abcde', 'ABC') + expectPositive('abcXYdefghijKLmnopqrs', 'XYKL') + expectPositive('abc123', 'ABC') + expectPositive('abc123', 'ABC123') + expectPositive('abc!@#def', 'ABC') + }) +}) + +describe('Abbreviation - Negative Tests', () => { + test('it should fail to abbreviate or transform the source string when it is not possible to match the target string', () => { + expectNegative('', 'A') + expectNegative('a', 'ABC') + expectNegative('aBcXYdefghijKLmnOpqrs', 'XYKLOP') }) -}) \ No newline at end of file +}) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy