diff --git a/src/main/java/com/company/text/SearchableString.java b/src/main/java/com/company/text/SearchableString.java new file mode 100644 index 0000000..21bf12f --- /dev/null +++ b/src/main/java/com/company/text/SearchableString.java @@ -0,0 +1,74 @@ +package com.company.text; + +import java.util.HashMap; +import java.util.Map; + +public class SearchableString { + public final String text; + public Map integerIntegerMap; + + public SearchableString(String text) { + this.text = text; + } + + public int searchText(String textToSearch) { + integerIntegerMap = this.buildIncrementMap(textToSearch); + System.out.println("\nStart to search " + textToSearch + " in " + this.text); + int count = 0; + for (int startIndex = 0; startIndex < text.length() - textToSearch.length() + 1; ) { + int matchedCharCount = 0; + System.out.println("startIndex: " + startIndex); + for (int currIndex = 0; currIndex < textToSearch.length(); ) { + char ch1 = text.charAt(currIndex + startIndex); + char ch2 = textToSearch.charAt(currIndex); + if (ch1 != ch2) { + break; + } + matchedCharCount++; + currIndex++; + } + if (matchedCharCount == textToSearch.length()) { + count++; + System.out.println("all matched in " + startIndex); + startIndex = startIndex + this.integerIntegerMap.get(matchedCharCount); + } else if (matchedCharCount == 0 || matchedCharCount == 1) { + startIndex++; + } else { + startIndex = startIndex + this.integerIntegerMap.get(matchedCharCount); + } + } + return count; + } + + /** + * Referencing KMP algorithm + * Build a start-index-increment map when searching text + * Key: length of first matching chars, Value: How many a start index increments. + * + * @param text to build + * @return a start-index-increment map + */ + Map buildIncrementMap(String text) { + System.out.println("IncrementMap build started for " + text); + HashMap incrementMap = new HashMap<>(); + for (int startIndex = 1; startIndex < text.length(); ) { + int currentIndex = startIndex; + for (; currentIndex < text.length(); currentIndex++) { + char ch1 = text.charAt(currentIndex - startIndex); + char leftCutText = text.charAt(currentIndex); + int partialMatchingLength = currentIndex + 1; + if (ch1 == leftCutText) { + incrementMap.put(partialMatchingLength, startIndex); + System.out.println("matchedLength: " + partialMatchingLength + " increment: " + startIndex); + } else { + incrementMap.put(currentIndex + 1, 1); + System.out.println("matchedLength: " + partialMatchingLength + " increment: " + 1); + break; + } + } + startIndex = currentIndex + 1; + } + System.out.println("IncrementMap has been built."); + return incrementMap; + } +} diff --git a/src/test/java/com/company/text/SearchableStringTest.java b/src/test/java/com/company/text/SearchableStringTest.java new file mode 100644 index 0000000..5174678 --- /dev/null +++ b/src/test/java/com/company/text/SearchableStringTest.java @@ -0,0 +1,79 @@ +package com.company.text; + +import org.junit.jupiter.api.Test; + +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +public class SearchableStringTest { + @Test + public void searchText_whenSearchingAVAInAVAVA_thenReturning2() { + // Given + String textToSearch = "ava"; + SearchableString avava = new SearchableString("avava"); + + // When + int count = avava.searchText(textToSearch); + + // Then + assertThat(count).isEqualTo(2); + } + + @Test + public void buildIncrementMap_whenAAIsGiven_thenIncrementHasNoElement() { + // Given + String textToSearch = "aa"; + SearchableString avava = new SearchableString(""); + + // When + Map aaIncrementMap = avava.buildIncrementMap(textToSearch); + + // Then + assertThat(aaIncrementMap).hasSize(1); + assertThat(aaIncrementMap.get(2)).isEqualTo(1); + } + + @Test + public void buildIncrementMap_when_ABcAB_IsGiven() { + // Given + String textToSearch = "abcab"; + SearchableString searchableString = new SearchableString(""); + + // When + Map incrementMap = searchableString.buildIncrementMap(textToSearch); + + // Then + assertThat(incrementMap).hasSize(4); + assertThat(incrementMap.get(2)).isEqualTo(1); + assertThat(incrementMap.get(3)).isEqualTo(1); + assertThat(incrementMap.get(4)).isEqualTo(3); + assertThat(incrementMap.get(5)).isEqualTo(3); + } + + @Test + public void searchText_whenSearchableTextContainsTheGivenStringIn2SubString_thenReturning2_case1() { + // Given + String textToSearch = "abcabd"; + SearchableString searchableString = new SearchableString("XXXX" + textToSearch + "XXXX" + textToSearch); + + // When + int matchedCount = searchableString.searchText(textToSearch); + + // Then + assertThat(matchedCount).isEqualTo(2); + } + + @Test + public void searchText_whenSearchableTextContainsTheGivenStringIn2SubString_thenReturning2_case2() { + // Given + String textToSearch = "abcabd"; + SearchableString searchableString = new SearchableString("XXXXabc" + textToSearch + "XXXX" + textToSearch); + + // When + int matchedCount = searchableString.searchText(textToSearch); + + // Then + assertThat(matchedCount).isEqualTo(2); + } +} \ No newline at end of file