diff --git a/src/algorithms/math/catalan-number/README.md b/src/algorithms/math/catalan-number/README.md
new file mode 100644
index 0000000000..2a0c92bcd0
--- /dev/null
+++ b/src/algorithms/math/catalan-number/README.md
@@ -0,0 +1,75 @@
+# Catalan Number
+
+In combinatorial mathematics, the **Catalan numbers** form a sequence of natural numbers that occur in various counting problems, often involving recursively-defined objects.
+
+The Catalan numbers on nonnegative integers `n` are a sequence `Cn` which begins:
+
+```
+C0=1, C1=1, C2=2, C3=5, C4=14, C5=42, C6=132, C7=429, C8=1430, C9=4862, C10=16796, ...
+```
+
+## Formula
+
+The nth Catalan number can be expressed directly in terms of binomial coefficients:
+
+
+
+```
+C(n) = (2n)! / ((n + 1)! * n!)
+```
+
+Or using the recursive formula:
+
+```
+C(0) = 1
+C(n) = sum of C(i) * C(n-1-i) for i = 0 to n-1
+```
+
+## Applications
+
+Catalan numbers have many applications in combinatorics:
+
+1. **Binary Search Trees**: `Cn` is the number of different binary search trees with `n` keys
+2. **Parentheses**: `Cn` is the number of expressions containing `n` pairs of correctly matched parentheses
+3. **Polygon Triangulation**: `Cn` is the number of ways to triangulate a convex polygon with `n+2` sides
+4. **Path Counting**: `Cn` is the number of paths in a grid from `(0,0)` to `(n,n)` that don't cross the diagonal
+5. **Dyck Words**: `Cn` is the number of Dyck words of length `2n`
+6. **Mountain Ranges**: `Cn` is the number of "mountain ranges" you can draw using `n` upstrokes and `n` downstrokes
+
+## Examples
+
+### Binary Search Trees
+With 3 keys (1, 2, 3), there are `C3 = 5` different BSTs:
+
+```
+ 1 1 2 3 3
+ \ \ / \ / /
+ 2 3 1 3 1 2
+ \ / \ /
+ 3 2 2 1
+```
+
+### Parentheses
+With 3 pairs of parentheses, there are `C3 = 5` valid combinations:
+
+```
+((()))
+(()())
+(())()
+()(())
+()()()
+```
+
+### Polygon Triangulation
+A hexagon (6 sides = n+2, so n=4) can be triangulated in `C4 = 14` different ways.
+
+## Complexity
+
+- **Time Complexity**: `O(n²)` - Using dynamic programming approach
+- **Space Complexity**: `O(n)` - To store intermediate results
+
+## References
+
+- [Wikipedia - Catalan Number](https://en.wikipedia.org/wiki/Catalan_number)
+- [OEIS - Catalan Numbers](https://oeis.org/A000108)
+- [YouTube - Catalan Numbers](https://www.youtube.com/watch?v=GlI17WaMrtw)
diff --git a/src/algorithms/math/catalan-number/__test__/catalanNumber.test.js b/src/algorithms/math/catalan-number/__test__/catalanNumber.test.js
new file mode 100644
index 0000000000..ef855fa29c
--- /dev/null
+++ b/src/algorithms/math/catalan-number/__test__/catalanNumber.test.js
@@ -0,0 +1,27 @@
+import catalanNumber from '../catalanNumber';
+
+describe('catalanNumber', () => {
+ it('should calculate Catalan numbers correctly', () => {
+ expect(catalanNumber(0)).toBe(1);
+ expect(catalanNumber(1)).toBe(1);
+ expect(catalanNumber(2)).toBe(2);
+ expect(catalanNumber(3)).toBe(5);
+ expect(catalanNumber(4)).toBe(14);
+ expect(catalanNumber(5)).toBe(42);
+ expect(catalanNumber(6)).toBe(132);
+ expect(catalanNumber(7)).toBe(429);
+ expect(catalanNumber(8)).toBe(1430);
+ expect(catalanNumber(9)).toBe(4862);
+ expect(catalanNumber(10)).toBe(16796);
+ });
+
+ it('should throw error for negative numbers', () => {
+ expect(() => catalanNumber(-1)).toThrow('Catalan number is not defined for negative numbers');
+ expect(() => catalanNumber(-10)).toThrow('Catalan number is not defined for negative numbers');
+ });
+
+ it('should handle larger numbers', () => {
+ expect(catalanNumber(15)).toBe(9694845);
+ expect(catalanNumber(20)).toBe(6564120420);
+ });
+});
diff --git a/src/algorithms/math/catalan-number/catalanNumber.js b/src/algorithms/math/catalan-number/catalanNumber.js
new file mode 100644
index 0000000000..0eb0b0714d
--- /dev/null
+++ b/src/algorithms/math/catalan-number/catalanNumber.js
@@ -0,0 +1,48 @@
+/**
+ * Calculate nth Catalan number using dynamic programming approach.
+ *
+ * Catalan numbers form a sequence of natural numbers that occur in various
+ * counting problems, often involving recursively-defined objects.
+ *
+ * The nth Catalan number can be expressed directly in terms of binomial coefficients:
+ * C(n) = (2n)! / ((n + 1)! * n!)
+ *
+ * Or using the recursive formula:
+ * C(0) = 1
+ * C(n) = sum of C(i) * C(n-1-i) for i = 0 to n-1
+ *
+ * Applications:
+ * - Number of different Binary Search Trees with n keys
+ * - Number of expressions containing n pairs of parentheses
+ * - Number of ways to triangulate a polygon with n+2 sides
+ * - Number of paths in a grid from (0,0) to (n,n) without crossing diagonal
+ *
+ * @param {number} n - The position in Catalan sequence
+ * @return {number} - The nth Catalan number
+ */
+export default function catalanNumber(n) {
+ // Handle edge cases
+ if (n < 0) {
+ throw new Error('Catalan number is not defined for negative numbers');
+ }
+
+ // Base case
+ if (n === 0 || n === 1) {
+ return 1;
+ }
+
+ // Use dynamic programming to calculate Catalan number
+ // This approach has O(n^2) time complexity and O(n) space complexity
+ const catalan = new Array(n + 1).fill(0);
+ catalan[0] = 1;
+ catalan[1] = 1;
+
+ // Calculate Catalan numbers from 2 to n
+ for (let i = 2; i <= n; i += 1) {
+ for (let j = 0; j < i; j += 1) {
+ catalan[i] += catalan[j] * catalan[i - 1 - j];
+ }
+ }
+
+ return catalan[n];
+}
diff --git a/src/algorithms/sorting/pancake-sort/PancakeSort.js b/src/algorithms/sorting/pancake-sort/PancakeSort.js
new file mode 100644
index 0000000000..592d0b558e
--- /dev/null
+++ b/src/algorithms/sorting/pancake-sort/PancakeSort.js
@@ -0,0 +1,63 @@
+/* eslint-disable no-param-reassign */
+import Sort from '../Sort';
+
+export default class PancakeSort extends Sort {
+ /**
+ * Flip the array from index 0 to index i
+ * @param {*[]} array
+ * @param {number} endIndex
+ */
+ flip(array, endIndex) {
+ let start = 0;
+ let end = endIndex;
+ while (start < end) {
+ // Swap elements
+ [array[start], array[end]] = [array[end], array[start]];
+ start += 1;
+ end -= 1;
+ }
+ }
+
+ /**
+ * Find the index of the maximum element in array[0...n]
+ * @param {*[]} array
+ * @param {number} n
+ * @return {number}
+ */
+ findMaxIndex(array, n) {
+ let maxIndex = 0;
+ for (let i = 1; i <= n; i += 1) {
+ // Call visiting callback.
+ this.callbacks.visitingCallback(array[i]);
+
+ if (this.comparator.greaterThan(array[i], array[maxIndex])) {
+ maxIndex = i;
+ }
+ }
+ return maxIndex;
+ }
+
+ sort(originalArray) {
+ // Clone original array to prevent its modification.
+ const array = [...originalArray];
+
+ // Start from the complete array and one by one reduce current size by one
+ for (let currentSize = array.length - 1; currentSize > 0; currentSize -= 1) {
+ // Find index of the maximum element in array[0...currentSize]
+ const maxIndex = this.findMaxIndex(array, currentSize);
+
+ // Move the maximum element to end of current array if it's not already at the end
+ if (maxIndex !== currentSize) {
+ // First move maximum number to beginning if it's not already
+ if (maxIndex !== 0) {
+ this.flip(array, maxIndex);
+ }
+
+ // Now move the maximum number to end by reversing current array
+ this.flip(array, currentSize);
+ }
+ }
+
+ return array;
+ }
+}
diff --git a/src/algorithms/sorting/pancake-sort/README.md b/src/algorithms/sorting/pancake-sort/README.md
new file mode 100644
index 0000000000..9e911c4d01
--- /dev/null
+++ b/src/algorithms/sorting/pancake-sort/README.md
@@ -0,0 +1,42 @@
+# Pancake Sort
+
+Pancake sorting is a sorting algorithm in which the only allowed operation is to "flip" one end of the list. The goal is to sort the array by repeatedly flipping portions of it.
+
+The algorithm is inspired by the real-world problem of sorting a stack of pancakes by size using a spatula. A flip operation reverses the order of elements from the beginning of the array up to a specified index.
+
+
+
+## Algorithm
+
+The algorithm works by repeatedly finding the maximum element and moving it to its correct position at the end of the array through a series of flips:
+
+1. Find the maximum element in the unsorted portion of the array
+2. If it's not at the beginning, flip it to the beginning
+3. Flip it to its correct position at the end
+4. Reduce the size of the unsorted portion and repeat
+
+## Example
+
+Given an array: `[3, 6, 2, 8, 4, 5]`
+
+**Step 1:** Find max (8) at index 3
+- Flip to beginning: `[8, 6, 3, 2, 4, 5]`
+- Flip to end: `[5, 4, 2, 3, 6, 8]`
+
+**Step 2:** Find max (6) at index 4
+- Flip to beginning: `[6, 4, 2, 3, 5, 8]`
+- Flip to position 4: `[5, 3, 2, 4, 6, 8]`
+
+**Step 3:** Continue until sorted: `[2, 3, 4, 5, 6, 8]`
+
+## Complexity
+
+| Name | Best | Average | Worst | Memory | Stable | Comments |
+| --------------------- | :-------------: | :-----------------: | :-----------------: | :-------: | :-------: | :-------- |
+| **Pancake sort** | n | n2 | n2 | 1 | No | |
+
+## References
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Pancake_sorting)
+- [GeeksforGeeks](https://www.geeksforgeeks.org/pancake-sorting/)
+- [YouTube](https://www.youtube.com/watch?v=kk-_DDgoXfg)
diff --git a/src/algorithms/sorting/pancake-sort/__test__/PancakeSort.test.js b/src/algorithms/sorting/pancake-sort/__test__/PancakeSort.test.js
new file mode 100644
index 0000000000..414052f190
--- /dev/null
+++ b/src/algorithms/sorting/pancake-sort/__test__/PancakeSort.test.js
@@ -0,0 +1,60 @@
+import PancakeSort from '../PancakeSort';
+import {
+ equalArr,
+ notSortedArr,
+ reverseArr,
+ sortedArr,
+ SortTester,
+} from '../../SortTester';
+
+// Complexity constants.
+const SORTED_ARRAY_VISITING_COUNT = 190;
+const NOT_SORTED_ARRAY_VISITING_COUNT = 190;
+const REVERSE_SORTED_ARRAY_VISITING_COUNT = 190;
+const EQUAL_ARRAY_VISITING_COUNT = 190;
+
+describe('PancakeSort', () => {
+ it('should sort array', () => {
+ SortTester.testSort(PancakeSort);
+ });
+
+ it('should sort array with custom comparator', () => {
+ SortTester.testSortWithCustomComparator(PancakeSort);
+ });
+
+ it('should sort negative numbers', () => {
+ SortTester.testNegativeNumbersSort(PancakeSort);
+ });
+
+ it('should visit EQUAL array element specified number of times', () => {
+ SortTester.testAlgorithmTimeComplexity(
+ PancakeSort,
+ equalArr,
+ EQUAL_ARRAY_VISITING_COUNT,
+ );
+ });
+
+ it('should visit SORTED array element specified number of times', () => {
+ SortTester.testAlgorithmTimeComplexity(
+ PancakeSort,
+ sortedArr,
+ SORTED_ARRAY_VISITING_COUNT,
+ );
+ });
+
+ it('should visit NOT SORTED array element specified number of times', () => {
+ SortTester.testAlgorithmTimeComplexity(
+ PancakeSort,
+ notSortedArr,
+ NOT_SORTED_ARRAY_VISITING_COUNT,
+ );
+ });
+
+ it('should visit REVERSE SORTED array element specified number of times', () => {
+ SortTester.testAlgorithmTimeComplexity(
+ PancakeSort,
+ reverseArr,
+ REVERSE_SORTED_ARRAY_VISITING_COUNT,
+ );
+ });
+});
diff --git a/src/algorithms/string/longest-common-subsequence/README.md b/src/algorithms/string/longest-common-subsequence/README.md
new file mode 100644
index 0000000000..db22972ee8
--- /dev/null
+++ b/src/algorithms/string/longest-common-subsequence/README.md
@@ -0,0 +1,101 @@
+# Longest Common Subsequence (LCS)
+
+The **Longest Common Subsequence (LCS)** problem is finding the longest subsequence common to two sequences. A subsequence is a sequence that can be derived from another sequence by deleting some or no elements without changing the order of the remaining elements.
+
+## Definition
+
+Given two sequences `X` and `Y`, a sequence `Z` is a common subsequence of `X` and `Y` if `Z` is a subsequence of both `X` and `Y`.
+
+For example:
+- If `X = "ABCDGH"` and `Y = "AEDFHR"`, then `"ADH"` is a common subsequence with length 3
+- If `X = "AGGTAB"` and `Y = "GXTXAYB"`, then `"GTAB"` is a common subsequence with length 4
+
+## Difference from Longest Common Substring
+
+**Important distinction:**
+- **Subsequence**: Characters don't need to be contiguous (can skip characters)
+- **Substring**: Characters must be contiguous (no gaps allowed)
+
+Example:
+- For strings "ABCDGH" and "AEDFHR":
+ - LCS (subsequence): "ADH" ✓ (can skip characters)
+ - LCS (substring): "" (no common contiguous substring of length > 1)
+
+## Algorithm
+
+This implementation uses **dynamic programming** with a bottom-up approach:
+
+1. Create a 2D table `dp[m+1][n+1]` where `m` and `n` are lengths of the two strings
+2. Fill the table using the recurrence relation:
+ ```
+ dp[i][j] = dp[i-1][j-1] + 1 if str1[i-1] == str2[j-1]
+ dp[i][j] = max(dp[i-1][j], dp[i][j-1]) otherwise
+ ```
+3. Backtrack from `dp[m][n]` to construct the actual LCS string
+
+## Complexity
+
+- **Time Complexity**: `O(m * n)` where `m` and `n` are the lengths of the two strings
+- **Space Complexity**: `O(m * n)` for the DP table
+
+## Applications
+
+The LCS algorithm has many practical applications:
+
+1. **Version Control Systems**: Git uses LCS-based algorithms to compute diffs between file versions
+2. **Bioinformatics**: DNA sequence alignment and comparison
+3. **Plagiarism Detection**: Finding similar content between documents
+4. **File Comparison**: Tools like `diff` use LCS to show differences
+5. **Data Synchronization**: Determining minimal changes needed to sync data
+6. **Spell Checkers**: Finding similar words for suggestions
+
+## Examples
+
+### Example 1: Basic LCS
+```javascript
+longestCommonSubsequence('ABCDGH', 'AEDFHR');
+// Returns: 'ADH'
+// Explanation: A-D-H are common in both strings in order
+```
+
+### Example 2: Programming Terms
+```javascript
+longestCommonSubsequence('algorithms', 'logarithm');
+// Returns: 'lgrithm'
+```
+
+### Example 3: No Common Subsequence
+```javascript
+longestCommonSubsequence('ABC', 'DEF');
+// Returns: ''
+```
+
+### Example 4: One String is Subsequence of Another
+```javascript
+longestCommonSubsequence('ABCDEF', 'ACE');
+// Returns: 'ACE'
+```
+
+## Visualization
+
+For strings `X = "AGGTAB"` and `Y = "GXTXAYB"`:
+
+```
+ "" G X T X A Y B
+"" 0 0 0 0 0 0 0 0
+A 0 0 0 0 0 1 1 1
+G 0 1 1 1 1 1 1 1
+G 0 1 1 1 1 1 1 1
+T 0 1 1 2 2 2 2 2
+A 0 1 1 2 2 3 3 3
+B 0 1 1 2 2 3 3 4
+```
+
+The value at `dp[6][7] = 4` indicates the LCS length is 4.
+Backtracking gives us: `"GTAB"`
+
+## References
+
+- [Wikipedia - Longest Common Subsequence](https://en.wikipedia.org/wiki/Longest_common_subsequence_problem)
+- [GeeksforGeeks - LCS](https://www.geeksforgeeks.org/longest-common-subsequence-dp-4/)
+- [YouTube - Dynamic Programming LCS](https://www.youtube.com/watch?v=ASoaQq66foQ)
diff --git a/src/algorithms/string/longest-common-subsequence/__test__/longestCommonSubsequence.test.js b/src/algorithms/string/longest-common-subsequence/__test__/longestCommonSubsequence.test.js
new file mode 100644
index 0000000000..e1f1f1f84b
--- /dev/null
+++ b/src/algorithms/string/longest-common-subsequence/__test__/longestCommonSubsequence.test.js
@@ -0,0 +1,42 @@
+import longestCommonSubsequence from '../longestCommonSubsequence';
+
+describe('longestCommonSubsequence', () => {
+ it('should find LCS of two strings', () => {
+ expect(longestCommonSubsequence('', '')).toBe('');
+ expect(longestCommonSubsequence('ABC', '')).toBe('');
+ expect(longestCommonSubsequence('', 'ABC')).toBe('');
+ expect(longestCommonSubsequence('A', 'A')).toBe('A');
+ expect(longestCommonSubsequence('ABC', 'ABC')).toBe('ABC');
+ expect(longestCommonSubsequence('ABCDGH', 'AEDFHR')).toBe('ADH');
+ expect(longestCommonSubsequence('AGGTAB', 'GXTXAYB')).toBe('GTAB');
+ expect(longestCommonSubsequence('sea', 'eat')).toBe('ea');
+ expect(longestCommonSubsequence('algorithms', 'logarithm')).toBe('lorithm');
+ });
+
+ it('should handle strings with no common subsequence', () => {
+ expect(longestCommonSubsequence('ABC', 'DEF')).toBe('');
+ expect(longestCommonSubsequence('XYZ', 'PQR')).toBe('');
+ });
+
+ it('should handle strings where one is subsequence of another', () => {
+ expect(longestCommonSubsequence('ABCDEF', 'ACE')).toBe('ACE');
+ expect(longestCommonSubsequence('ACE', 'ABCDEF')).toBe('ACE');
+ });
+
+ it('should handle repeated characters', () => {
+ expect(longestCommonSubsequence('AAA', 'AA')).toBe('AA');
+ expect(longestCommonSubsequence('AAAA', 'AA')).toBe('AA');
+ expect(longestCommonSubsequence('ABABA', 'BABA')).toBe('BABA');
+ });
+
+ it('should handle case sensitivity', () => {
+ expect(longestCommonSubsequence('ABC', 'abc')).toBe('');
+ expect(longestCommonSubsequence('Hello', 'hello')).toBe('ello');
+ });
+
+ it('should handle longer strings', () => {
+ expect(longestCommonSubsequence('ABCBDAB', 'BDCABA')).toBe('BDAB');
+ expect(longestCommonSubsequence('programming', 'gaming')).toBe('gaming');
+ expect(longestCommonSubsequence('dynamic', 'programming')).toBe('ami');
+ });
+});
diff --git a/src/algorithms/string/longest-common-subsequence/longestCommonSubsequence.js b/src/algorithms/string/longest-common-subsequence/longestCommonSubsequence.js
new file mode 100644
index 0000000000..54e0da3ceb
--- /dev/null
+++ b/src/algorithms/string/longest-common-subsequence/longestCommonSubsequence.js
@@ -0,0 +1,74 @@
+/**
+ * Find the longest common subsequence (LCS) of two strings.
+ *
+ * A subsequence is a sequence that can be derived from another sequence
+ * by deleting some or no elements without changing the order of the
+ * remaining elements.
+ *
+ * For example:
+ * - LCS of "ABCDGH" and "AEDFHR" is "ADH" (length 3)
+ * - LCS of "AGGTAB" and "GXTXAYB" is "GTAB" (length 4)
+ *
+ * This implementation uses dynamic programming with O(m*n) time complexity
+ * and O(m*n) space complexity, where m and n are the lengths of the two strings.
+ *
+ * Applications:
+ * - Diff utilities (comparing file versions)
+ * - DNA sequence analysis
+ * - Version control systems
+ * - Plagiarism detection
+ * - Data comparison tools
+ *
+ * @param {string} str1 - First string
+ * @param {string} str2 - Second string
+ * @return {string} - The longest common subsequence
+ */
+export default function longestCommonSubsequence(str1, str2) {
+ // Handle edge cases
+ if (!str1 || !str2) {
+ return '';
+ }
+
+ const m = str1.length;
+ const n = str2.length;
+
+ // Create a 2D array to store lengths of LCS
+ // dp[i][j] contains length of LCS of str1[0..i-1] and str2[0..j-1]
+ const dp = Array(m + 1)
+ .fill(null)
+ .map(() => Array(n + 1).fill(0));
+
+ // Build the dp table in bottom-up fashion
+ for (let i = 1; i <= m; i += 1) {
+ for (let j = 1; j <= n; j += 1) {
+ if (str1[i - 1] === str2[j - 1]) {
+ // If characters match, add 1 to the result of smaller strings
+ dp[i][j] = dp[i - 1][j - 1] + 1;
+ } else {
+ // If characters don't match, take the maximum of two possibilities
+ dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
+ }
+ }
+ }
+
+ // Backtrack to find the actual LCS string
+ let i = m;
+ let j = n;
+ const lcs = [];
+
+ while (i > 0 && j > 0) {
+ if (str1[i - 1] === str2[j - 1]) {
+ // If characters match, it's part of LCS
+ lcs.unshift(str1[i - 1]);
+ i -= 1;
+ j -= 1;
+ } else if (dp[i - 1][j] > dp[i][j - 1]) {
+ // Move in the direction of larger value
+ i -= 1;
+ } else {
+ j -= 1;
+ }
+ }
+
+ return lcs.join('');
+}