Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 95 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2193,6 +2193,101 @@ public class PalindromCheckSnippet {
}
}
```
### Rabin-Karp Substring Search Algorithm

```java
public class RabinKarpSubstringSearchSnippet {

private static final int PRIME = 1_000_000_007; // Large prime to reduce collisions

/**
* Implements the Rabin-Karp algorithm to find the index of the first occurrence
* of a substring in a given text.
*
* @param text The text in which the substring is to be searched.
* @param pattern The substring pattern to search for.
* @return The index of the first occurrence of the pattern in the text,
* or -1 if the pattern is not found.
*/
public static int rabinKarpSearch(String text, String pattern) {
if (pattern == null || pattern.length() == 0) {
return 0; // Trivial case: empty pattern
}
if (text == null || text.length() < pattern.length()) {
return -1; // Pattern cannot be found
}

int m = pattern.length();
int n = text.length();
long patternHash = createHash(pattern, m);
long textHash = createHash(text, m);

for (int i = 0; i <= n - m; i++) {
if (patternHash == textHash && checkEqual(text, i, i + m - 1, pattern, 0, m - 1)) {
return i; // Match found
}
if (i < n - m) {
textHash = recalculateHash(text, i, i + m, textHash, m);
}
}
return -1; // No match found
}

/**
* Creates a hash for the given substring.
*
* @param str The string to hash.
* @param end The length of substring considered.
* @return The hash value.
*/
private static long createHash(String str, int end) {
long hash = 0;
for (int i = 0; i < end; i++) {
hash += (long) str.charAt(i) * pow(PRIME, i);
}
return hash;
}

/**
* Recalculates hash by sliding the window by one character.
*/
private static long recalculateHash(String str, int oldIndex, int newIndex, long oldHash, int patternLen) {
long newHash = oldHash - str.charAt(oldIndex);
newHash /= PRIME;
newHash += (long) str.charAt(newIndex) * pow(PRIME, patternLen - 1);
return newHash;
}

/**
* Checks if two substrings are equal.
*/
private static boolean checkEqual(String str1, int start1, int end1, String str2, int start2, int end2) {
if (end1 - start1 != end2 - start2) {
return false;
}
while (start1 <= end1 && start2 <= end2) {
if (str1.charAt(start1) != str2.charAt(start2)) {
return false;
}
start1++;
start2++;
}
return true;
}

/**
* Efficient power function for modular hashing.
*/
private static long pow(int base, int exp) {
long result = 1;
for (int i = 0; i < exp; i++) {
result *= base;
}
return result;
}
}
```


### Reverse String

Expand Down
132 changes: 132 additions & 0 deletions src/main/java/string/RabinKarpSubstringSearchSnippet.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/*
* MIT License
*
* Copyright (c) 2017-2024 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

package string;

/**
* RabinKarpSubstringSearchSnippet.
*/
public class RabinKarpSubstringSearchSnippet {

private static final int PRIME = 1_000_000_007; // A prime number used as modulus for hashing

/**
* Implements the Rabin-Karp algorithm to find the index of a substring.
*
* @param text The text in which the substring is to be searched.
* @param pattern The substring pattern to search for.
* @return The index of the first occurrence, or -1 if the pattern is not found.
*/
public static int rabinKarpSearch(String text, String pattern) {
if (pattern == null || pattern.length() == 0) {
return 0; // Trivial case: empty pattern
}
if (text == null || text.length() < pattern.length()) {
return -1; // Pattern cannot be found
}

int m = pattern.length();
int n = text.length();
long patternHash = createHash(pattern, m);
long textHash = createHash(text, m);

for (int i = 0; i <= n - m; i++) {
if (patternHash == textHash && checkEqual(text, i, i + m - 1, pattern, 0, m - 1)) {
return i; // Match found
}
if (i < n - m) {
textHash = recalculateHash(text, i, i + m, textHash, m);
}
}
return -1; // No match found
}

/**
* Creates a hash for the given substring.
*
* @param str The string to hash.
* @param end The length of substring considered.
* @return The hash value.
*/
private static long createHash(String str, int end) {
long hash = 0;
for (int i = 0; i < end; i++) {
hash += str.charAt(i) * Math.pow(PRIME, i);
}
return hash;
}

/**
* Recalculates hash by sliding the window by one character.
*
* @param str The original string.
* @param oldIndex The index of the outgoing character.
* @param newIndex The index of the incoming character.
* @param oldHash The previous hash value.
* @param patternLen The length of the pattern.
* @return The recalculated hash.
*/
private static long recalculateHash(
String str,
int oldIndex,
int newIndex,
long oldHash,
int patternLen) {
long newHash = oldHash - str.charAt(oldIndex);
newHash /= PRIME;
newHash += str.charAt(newIndex) * Math.pow(PRIME, patternLen - 1);
return newHash;
}

/**
* Checks if two substrings are equal.
*
* @param str1 The first string.
* @param start1 Start index in str1.
* @param end1 End index in str1.
* @param str2 The second string.
* @param start2 Start index in str2.
* @param end2 End index in str2.
* @return True if substrings are equal, otherwise false.
*/
private static boolean checkEqual(
String str1,
int start1,
int end1,
String str2,
int start2,
int end2) {
if (end1 - start1 != end2 - start2) {
return false;
}
while (start1 <= end1 && start2 <= end2) {
if (str1.charAt(start1) != str2.charAt(start2)) {
return false;
}
start1++;
start2++;
}
return true;
}
}
49 changes: 49 additions & 0 deletions src/test/java/string/RabinKarpSubstringSearchSnippetTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* MIT License
*
* Copyright (c) 2017-2024 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

package string;

import static org.junit.jupiter.api.Assertions.assertEquals;

import org.junit.jupiter.api.Test;

/**
* Tests for 30 Seconds of Java code library.
*
*/
class RabinKarpSubstringSearchSnippetTest {

/**
* Tests for {@link RabinKarpSubstringSearchSnippet#rabinKarpSearch(String, String)}.
*/
@Test
void testRabinKarpSearch() {
// Test cases for Rabin-Karp substring search
assertEquals(6, RabinKarpSubstringSearchSnippet.rabinKarpSearch("abxabcabcaby", "abcaby"));
assertEquals(7, RabinKarpSubstringSearchSnippet.rabinKarpSearch("subash pandey", "pandey"));
assertEquals(-1, RabinKarpSubstringSearchSnippet.rabinKarpSearch("abcd", "e"));
assertEquals(0, RabinKarpSubstringSearchSnippet.rabinKarpSearch("aaaaa", "a"));
assertEquals(2, RabinKarpSubstringSearchSnippet.rabinKarpSearch("abcdabcd", "cdab"));
}
}
Loading