Working solution in Java:
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
String test1 = "fbrtfuifigfbrt";
String test2 = "abcdabcd";
String test3 = "fbrtxibrjkfbrt";
System.out.println(findRepetitions(test1));
System.out.println(findRepetitions(test2));
System.out.println(findRepetitions(test3));
}
private static List<String> findRepetitions(String string) {
List<String> patternsList = new ArrayList<>();
int length = string.length();
for (int i = 0; i < length; i++) { // search the first half
int limit = (length - i) / 2; // candidates can't be longer than half the remaining length
for (int j = 1; j <= limit; j++) {
int candidateEndIndex = i + j;
String candidate = string.substring(i, candidateEndIndex);
if (string.substring(candidateEndIndex).contains(candidate)) {
patternsList.add(candidate);
}
}
}
return patternsList;
}
}
Output:
[f, fb, fbr, fbrt, b, br, brt, r, rt, t, f, i, f]
[a, ab, abc, abcd, b, bc, bcd, c, cd, d]
[f, fb, fbr, fbrt, b, br, brt, r, rt, t, b, br, r]
As others already said, there's no easy optimization for this if you don't know the length of the pattern or any other applicable restriction.
If you wanted to naively discard subpatterns like f
, fb
, fbr
which are being counted just because they are substrings of the longest fbrt
pattern, you could make the inner for
count downwards, from limit
down to 1, so you would find longer patterns first, and then check if the next patterns are a substring of already found ones before adding them to the list. Like this:
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
String test1 = "fbrtfuifigfbrt";
String test2 = "abcdabcd";
String test3 = "fbrtxibrjkfbrt"; // "br" is a pattern but this version won't find it
System.out.println(findRepetitions(test1));
System.out.println(findRepetitions(test2));
System.out.println(findRepetitions(test3));
}
private static List<String> findRepetitions(String string) {
List<String> patternsList = new ArrayList<>();
int length = string.length();
for (int i = 0; i < length; i++) { // search the first half
int limit = (length - i) / 2; // candidates can't be longer than half the remaining length
for (int j = limit; j >= 1; j--) {
int candidateEndIndex = i + j;
String candidate = string.substring(i, candidateEndIndex);
if (string.substring(candidateEndIndex).contains(candidate)) {
boolean notASubpattern = true;
for (String pattern : patternsList) {
if (pattern.contains(candidate)) {
notASubpattern = false;
break;
}
}
if (notASubpattern) {
patternsList.add(candidate);
}
}
}
}
return patternsList;
}
}
This, however, would prevent you from finding br
in fbrtxzbrjkfbrt
, as shown by the output (and it'd make the algorithm slower for strings with a lot of different patterns, too):
[fbrt, i]
[abcd]
[fbrt]
Hence the naively part. Of course, you could include more inner loops to make sure to-be-discarded candidates aren't found "on their own" in the original string, before actually discarding them... etc. It depends on how exahustive you want your search to be.