From 76f7a428349f6a18c09eaf79d38dda121ed10418 Mon Sep 17 00:00:00 2001 From: Nithin U <106614289+NithinU2802@users.noreply.github.com> Date: Wed, 17 Sep 2025 09:57:38 +0530 Subject: [PATCH 01/10] adding Suffix Array Implementation --- .../thealgorithms/strings/SuffixArray.java | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 src/main/java/com/thealgorithms/strings/SuffixArray.java diff --git a/src/main/java/com/thealgorithms/strings/SuffixArray.java b/src/main/java/com/thealgorithms/strings/SuffixArray.java new file mode 100644 index 000000000000..ef388fda3c52 --- /dev/null +++ b/src/main/java/com/thealgorithms/strings/SuffixArray.java @@ -0,0 +1,57 @@ +package com.thealgorithm.strings; + +import java.util.Arrays; + +/** + * Suffix Array implementation in Java. + * Builds an array of indices that represent all suffixes of a string in sorted order. + * Wikipedia Reference: https://en.wikipedia.org/wiki/Suffix_array + * Author: Nithin U. + * Github: https://github.com/NithinU2802 + */ + +public final class SuffixArray { + + public static int[] buildSuffixArray(String text) { + int n = text.length(); + Integer[] suffixArray = new Integer[n]; + int[] rank = new int[n]; + int[] tempRank = new int[n]; + + // Initial ranking based on characters + for (int i = 0; i < n; i++) { + suffixArray[i] = i; + rank[i] = text.charAt(i); + } + + for (int k = 1; k < n; k *= 2) { + final int step = k; + + // Comparator: first by rank, then by rank + step + Arrays.sort(suffixArray, (a, b) -> { + if (rank[a] != rank[b]) + return Integer.compare(rank[a], rank[b]); + int ra = (a + step < n) ? rank[a + step] : -1; + int rb = (b + step < n) ? rank[b + step] : -1; + return Integer.compare(ra, rb); + }); + + // Re-rank + tempRank[suffixArray[0]] = 0; + for (int i = 1; i < n; i++) { + int prev = suffixArray[i - 1]; + int curr = suffixArray[i]; + boolean sameRank = rank[prev] == rank[curr] && + ((prev + step < n ? rank[prev + step] : -1) == (curr + step < n ? rank[curr + step] : -1)); + tempRank[curr] = sameRank ? tempRank[prev] : tempRank[prev] + 1; + } + + System.arraycopy(tempRank, 0, rank, 0, n); + + if (rank[suffixArray[n - 1]] == n - 1) + break; + } + return Arrays.stream(suffixArray).mapToInt(Integer::intValue).toArray(); + } + +} From 8a0e2b4333dc897f8efdd4d51d0faa7754745388 Mon Sep 17 00:00:00 2001 From: Nithin U <106614289+NithinU2802@users.noreply.github.com> Date: Wed, 17 Sep 2025 10:00:00 +0530 Subject: [PATCH 02/10] adding test for Suffix Array --- .../strings/SuffixArrayTest.java | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 src/test/java/com/thealgorithms/strings/SuffixArrayTest.java diff --git a/src/test/java/com/thealgorithms/strings/SuffixArrayTest.java b/src/test/java/com/thealgorithms/strings/SuffixArrayTest.java new file mode 100644 index 000000000000..364a875eba48 --- /dev/null +++ b/src/test/java/com/thealgorithms/strings/SuffixArrayTest.java @@ -0,0 +1,45 @@ +package com.thealgorithms.strings; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import org.junit.jupiter.api.Test; + +class SuffixArrayTest { + + @Test + void testEmptyString() { + int[] result = SuffixArray.buildSuffixArray(""); + assertArrayEquals(new int[] {}, result, "Empty string should return empty suffix array"); + } + + @Test + void testSingleCharacter() { + int[] result = SuffixArray.buildSuffixArray("a"); + assertArrayEquals(new int[] {0}, result, "Single char string should return [0]"); + } + + @Test + void testDistinctCharacters() { + int[] result = SuffixArray.buildSuffixArray("abc"); + assertArrayEquals(new int[] {0, 1, 2}, result, "Suffixes already in order for distinct chars"); + } + + @Test + void testBananaExample() { + int[] result = SuffixArray.buildSuffixArray("banana"); + assertArrayEquals(new int[] {5, 3, 1, 0, 4, 2}, result, "Suffix array of 'banana' should be [5,3,1,0,4,2]"); + } + + @Test + void testStringWithDuplicates() { + int[] result = SuffixArray.buildSuffixArray("aaaa"); + assertArrayEquals(new int[] {3, 2, 1, 0}, result, "Suffix array should be descending indices for 'aaaa'"); + } + + @Test + void testRandomString() { + int[] result = SuffixArray.buildSuffixArray("mississippi"); + assertArrayEquals(new int[] {10, 7, 4, 1, 0, 9, 8, 6, 3, 5, 2}, result, "Suffix array for 'mississippi' should match expected" + ); + } +} From b378cb55898b135506c23c66b53eb5a43b5a36c9 Mon Sep 17 00:00:00 2001 From: Nithin U <106614289+NithinU2802@users.noreply.github.com> Date: Wed, 17 Sep 2025 10:09:45 +0530 Subject: [PATCH 03/10] patch: lint fix in SuffixArray.java --- src/main/java/com/thealgorithms/strings/SuffixArray.java | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/thealgorithms/strings/SuffixArray.java b/src/main/java/com/thealgorithms/strings/SuffixArray.java index ef388fda3c52..30887cba6ab7 100644 --- a/src/main/java/com/thealgorithms/strings/SuffixArray.java +++ b/src/main/java/com/thealgorithms/strings/SuffixArray.java @@ -29,8 +29,7 @@ public static int[] buildSuffixArray(String text) { // Comparator: first by rank, then by rank + step Arrays.sort(suffixArray, (a, b) -> { - if (rank[a] != rank[b]) - return Integer.compare(rank[a], rank[b]); + if (rank[a] != rank[b]) return Integer.compare(rank[a], rank[b]); int ra = (a + step < n) ? rank[a + step] : -1; int rb = (b + step < n) ? rank[b + step] : -1; return Integer.compare(ra, rb); @@ -41,15 +40,13 @@ public static int[] buildSuffixArray(String text) { for (int i = 1; i < n; i++) { int prev = suffixArray[i - 1]; int curr = suffixArray[i]; - boolean sameRank = rank[prev] == rank[curr] && - ((prev + step < n ? rank[prev + step] : -1) == (curr + step < n ? rank[curr + step] : -1)); + boolean sameRank = rank[prev] == rank[curr] && ((prev + step < n ? rank[prev + step] : -1) == (curr + step < n ? rank[curr + step] : -1)); tempRank[curr] = sameRank ? tempRank[prev] : tempRank[prev] + 1; } System.arraycopy(tempRank, 0, rank, 0, n); - if (rank[suffixArray[n - 1]] == n - 1) - break; + if (rank[suffixArray[n - 1]] == n - 1) break; } return Arrays.stream(suffixArray).mapToInt(Integer::intValue).toArray(); } From a963879636b57c6c1adf1767fa942936da52a8fe Mon Sep 17 00:00:00 2001 From: Nithin U <106614289+NithinU2802@users.noreply.github.com> Date: Wed, 17 Sep 2025 10:10:46 +0530 Subject: [PATCH 04/10] patch: lint fix in SuffixArrayTest.java --- src/test/java/com/thealgorithms/strings/SuffixArrayTest.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/test/java/com/thealgorithms/strings/SuffixArrayTest.java b/src/test/java/com/thealgorithms/strings/SuffixArrayTest.java index 364a875eba48..9d4b3d582b75 100644 --- a/src/test/java/com/thealgorithms/strings/SuffixArrayTest.java +++ b/src/test/java/com/thealgorithms/strings/SuffixArrayTest.java @@ -39,7 +39,6 @@ void testStringWithDuplicates() { @Test void testRandomString() { int[] result = SuffixArray.buildSuffixArray("mississippi"); - assertArrayEquals(new int[] {10, 7, 4, 1, 0, 9, 8, 6, 3, 5, 2}, result, "Suffix array for 'mississippi' should match expected" - ); + assertArrayEquals(new int[] {10, 7, 4, 1, 0, 9, 8, 6, 3, 5, 2}, result, "Suffix array for 'mississippi' should match expected"); } } From d1506a43fdbf47f5cb6012b01ec7f369a7fdb52e Mon Sep 17 00:00:00 2001 From: Nithin U <106614289+NithinU2802@users.noreply.github.com> Date: Wed, 17 Sep 2025 10:28:43 +0530 Subject: [PATCH 05/10] fix: private constructor update --- src/main/java/com/thealgorithms/strings/SuffixArray.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/com/thealgorithms/strings/SuffixArray.java b/src/main/java/com/thealgorithms/strings/SuffixArray.java index 30887cba6ab7..be3a221877ae 100644 --- a/src/main/java/com/thealgorithms/strings/SuffixArray.java +++ b/src/main/java/com/thealgorithms/strings/SuffixArray.java @@ -12,6 +12,9 @@ public final class SuffixArray { + private SuffixArray() { + } + public static int[] buildSuffixArray(String text) { int n = text.length(); Integer[] suffixArray = new Integer[n]; From b525c5d5563b72656bb10b37347bcad2c333bdc7 Mon Sep 17 00:00:00 2001 From: Nithin U <106614289+NithinU2802@users.noreply.github.com> Date: Wed, 17 Sep 2025 17:23:05 +0530 Subject: [PATCH 06/10] patch: lint fix in suffix array impl --- src/main/java/com/thealgorithms/strings/SuffixArray.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/thealgorithms/strings/SuffixArray.java b/src/main/java/com/thealgorithms/strings/SuffixArray.java index be3a221877ae..0eaea6686b29 100644 --- a/src/main/java/com/thealgorithms/strings/SuffixArray.java +++ b/src/main/java/com/thealgorithms/strings/SuffixArray.java @@ -1,4 +1,4 @@ -package com.thealgorithm.strings; +package com.thealgorithms.strings; import java.util.Arrays; @@ -49,7 +49,9 @@ public static int[] buildSuffixArray(String text) { System.arraycopy(tempRank, 0, rank, 0, n); - if (rank[suffixArray[n - 1]] == n - 1) break; + if (rank[suffixArray[n - 1]] == n - 1) { + break; + } } return Arrays.stream(suffixArray).mapToInt(Integer::intValue).toArray(); } From 7c163684358bb5bc72d15b00dbaeac6f9b95901f Mon Sep 17 00:00:00 2001 From: Nithin U <106614289+NithinU2802@users.noreply.github.com> Date: Wed, 17 Sep 2025 17:30:15 +0530 Subject: [PATCH 07/10] fix: lint issue line blankspace removal --- src/main/java/com/thealgorithms/strings/SuffixArray.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/thealgorithms/strings/SuffixArray.java b/src/main/java/com/thealgorithms/strings/SuffixArray.java index 0eaea6686b29..45c2428708bb 100644 --- a/src/main/java/com/thealgorithms/strings/SuffixArray.java +++ b/src/main/java/com/thealgorithms/strings/SuffixArray.java @@ -55,5 +55,4 @@ public static int[] buildSuffixArray(String text) { } return Arrays.stream(suffixArray).mapToInt(Integer::intValue).toArray(); } - } From db6c63646b8345dff5a2f81f6ff2b3a6beb21b69 Mon Sep 17 00:00:00 2001 From: Nithin U <106614289+NithinU2802@users.noreply.github.com> Date: Wed, 17 Sep 2025 17:34:55 +0530 Subject: [PATCH 08/10] fix: checkstype issue --- src/main/java/com/thealgorithms/strings/SuffixArray.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/thealgorithms/strings/SuffixArray.java b/src/main/java/com/thealgorithms/strings/SuffixArray.java index 45c2428708bb..4b2a8c4487ec 100644 --- a/src/main/java/com/thealgorithms/strings/SuffixArray.java +++ b/src/main/java/com/thealgorithms/strings/SuffixArray.java @@ -32,7 +32,9 @@ public static int[] buildSuffixArray(String text) { // Comparator: first by rank, then by rank + step Arrays.sort(suffixArray, (a, b) -> { - if (rank[a] != rank[b]) return Integer.compare(rank[a], rank[b]); + if (rank[a] != rank[b]){ + return Integer.compare(rank[a], rank[b]); + } int ra = (a + step < n) ? rank[a + step] : -1; int rb = (b + step < n) ? rank[b + step] : -1; return Integer.compare(ra, rb); From ed4b1c186cbe2ce40503b189aed15d8cf1f2a16c Mon Sep 17 00:00:00 2001 From: Nithin U <106614289+NithinU2802@users.noreply.github.com> Date: Wed, 17 Sep 2025 17:37:08 +0530 Subject: [PATCH 09/10] fix: lint format issue --- src/main/java/com/thealgorithms/strings/SuffixArray.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/thealgorithms/strings/SuffixArray.java b/src/main/java/com/thealgorithms/strings/SuffixArray.java index 4b2a8c4487ec..dbcac147b658 100644 --- a/src/main/java/com/thealgorithms/strings/SuffixArray.java +++ b/src/main/java/com/thealgorithms/strings/SuffixArray.java @@ -32,7 +32,7 @@ public static int[] buildSuffixArray(String text) { // Comparator: first by rank, then by rank + step Arrays.sort(suffixArray, (a, b) -> { - if (rank[a] != rank[b]){ + if (rank[a] != rank[b]) { return Integer.compare(rank[a], rank[b]); } int ra = (a + step < n) ? rank[a + step] : -1; From 64f2a989648ca70133d444d8cf0f5943f269e604 Mon Sep 17 00:00:00 2001 From: Nithin U <106614289+NithinU2802@users.noreply.github.com> Date: Wed, 17 Sep 2025 20:16:08 +0530 Subject: [PATCH 10/10] Update DIRECTORY.md --- DIRECTORY.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/DIRECTORY.md b/DIRECTORY.md index 549e166cc8a7..13d1fe39c168 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -744,6 +744,7 @@ - 📄 [Rotation](src/main/java/com/thealgorithms/strings/Rotation.java) - 📄 [StringCompression](src/main/java/com/thealgorithms/strings/StringCompression.java) - 📄 [StringMatchFiniteAutomata](src/main/java/com/thealgorithms/strings/StringMatchFiniteAutomata.java) + - 📄 [SuffixArray](src/main/java/com/thealgorithms/strings/SuffixArray.java) - 📄 [Upper](src/main/java/com/thealgorithms/strings/Upper.java) - 📄 [ValidParentheses](src/main/java/com/thealgorithms/strings/ValidParentheses.java) - 📄 [WordLadder](src/main/java/com/thealgorithms/strings/WordLadder.java) @@ -1413,6 +1414,7 @@ - 📄 [RotationTest](src/test/java/com/thealgorithms/strings/RotationTest.java) - 📄 [StringCompressionTest](src/test/java/com/thealgorithms/strings/StringCompressionTest.java) - 📄 [StringMatchFiniteAutomataTest](src/test/java/com/thealgorithms/strings/StringMatchFiniteAutomataTest.java) + - 📄 [SuffixArrayTest](src/test/java/com/thealgorithms/strings/SuffixArrayTest.java) - 📄 [UpperTest](src/test/java/com/thealgorithms/strings/UpperTest.java) - 📄 [ValidParenthesesTest](src/test/java/com/thealgorithms/strings/ValidParenthesesTest.java) - 📄 [WordLadderTest](src/test/java/com/thealgorithms/strings/WordLadderTest.java)