From 39b4681c6e1f174eb9aa9e68a9efe8b641e4e26a Mon Sep 17 00:00:00 2001 From: Rishavbhattarai Date: Mon, 26 Oct 2026 19:25:40 -0700 Subject: [PATCH] fix(strings): use case-insensitive shift table in HorspoolSearch.findFirstInsensitive --- .../thealgorithms/strings/HorspoolSearch.java | 18 +++++++++++------- .../strings/HorspoolSearchTest.java | 7 +++++++ 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/thealgorithms/strings/HorspoolSearch.java b/src/main/java/com/thealgorithms/strings/HorspoolSearch.java index d9187cbf66c4..51dcdd804b25 100644 --- a/src/main/java/com/thealgorithms/strings/HorspoolSearch.java +++ b/src/main/java/com/thealgorithms/strings/HorspoolSearch.java @@ -95,7 +95,7 @@ public static Integer getLastComparisons() { * @return index of first occurrence of the pattern in the text */ private static int firstOccurrence(String pattern, String text, boolean caseSensitive) { - shiftValues = calcShiftValues(pattern); // build the bad symbol table + shiftValues = calcShiftValues(pattern, caseSensitive); // build the bad symbol table comparisons = 0; // reset comparisons if (pattern.length() == 0) { // return failure, if pattern empty @@ -113,7 +113,7 @@ private static int firstOccurrence(String pattern, String text, boolean caseSens char patternChar = pattern.charAt(i); char textChar = text.charAt((textIndex + i) - (pattern.length() - 1)); if (!charEquals(patternChar, textChar, caseSensitive)) { // bad character, shift pattern - textIndex += getShiftValue(text.charAt(textIndex)); + textIndex += getShiftValue(text.charAt(textIndex), caseSensitive); break; } i--; @@ -153,14 +153,15 @@ private static boolean charEquals(char c1, char c2, boolean caseSensitive) { * in the table, then it is not a rightmost occurrence, so it continues. * * @param pattern basis for the bad symbol table + * @param caseSensitive whether to build the table with case-sensitive keys * @return the bad symbol table */ - private static HashMap calcShiftValues(String pattern) { + private static HashMap calcShiftValues(String pattern, boolean caseSensitive) { patternLength = pattern.length(); HashMap table = new HashMap<>(); for (int i = pattern.length() - 2; i >= 0; i--) { // length - 2 is the index of the second to last character - char c = pattern.charAt(i); + char c = caseSensitive ? pattern.charAt(i) : Character.toLowerCase(pattern.charAt(i)); int finalI = i; table.computeIfAbsent(c, k -> pattern.length() - 1 - finalI); } @@ -173,11 +174,14 @@ private static HashMap calcShiftValues(String pattern) { * appropriate shift value for a given character * * @param c character + * @param caseSensitive whether the table was built with case-sensitive keys * @return shift value that corresponds to the character argument */ - private static Integer getShiftValue(char c) { - if (shiftValues.get(c) != null) { - return shiftValues.get(c); + private static Integer getShiftValue(char c, boolean caseSensitive) { + // the table holds lower-case keys for case-insensitive searches + char key = caseSensitive ? c : Character.toLowerCase(c); + if (shiftValues.get(key) != null) { + return shiftValues.get(key); } else { return patternLength; } diff --git a/src/test/java/com/thealgorithms/strings/HorspoolSearchTest.java b/src/test/java/com/thealgorithms/strings/HorspoolSearchTest.java index 9649a89a3325..d364e4923e70 100644 --- a/src/test/java/com/thealgorithms/strings/HorspoolSearchTest.java +++ b/src/test/java/com/thealgorithms/strings/HorspoolSearchTest.java @@ -61,6 +61,13 @@ void testFindFirstInsensitiveNotMatch() { assertEquals(-1, index); } + @Test + void testFindFirstInsensitiveWhenCaseDiffersFromPattern() { + assertEquals(1, HorspoolSearch.findFirstInsensitive("AB", "xab")); + assertEquals(1, HorspoolSearch.findFirstInsensitive("ab", "xAB")); + assertEquals(2, HorspoolSearch.findFirstInsensitive("ba", "bbBA")); + } + @Test void testGetLastComparisons() { HorspoolSearch.findFirst("World", "Hello World");