import java.util.*;
import java.util.function.*;
class SINGLE_MATCH {
public static void main(String[] args) {
List<AlgExtend> algList = new ArrayList<>();
algList.add(AlgExtend.of(BF.class));
algList.add(AlgExtend.of(RK.class));
algList.add(AlgExtend.of(Sunday.class));
algList.add(AlgExtend.of(KMP.class));
algList.add(AlgExtend.of(BM.class));
algList.add(AlgExtend.of(BM.class, () -> {BM.type = 0; return " BAD";}));
algList.add(AlgExtend.of(BM.class, () -> {BM.type = 1; return " GOOD";}));
algList.forEach(alg -> alg.run());
}
public static class AlgExtend {
protected Class<?> algClass;
protected Supplier<Object> before;
protected Runnable after;
public static AlgExtend of (Class<?> algClass) {
return of(algClass, null);
}
public static AlgExtend of (Class<?> algClass, Supplier<Object> before) {
return of(algClass, before, null);
}
public static AlgExtend of (Class<?> algClass, Supplier<Object> before, Runnable after) {
assert algClass != null;
AlgExtend alg = new AlgExtend();
alg.algClass = algClass;
alg.before = before;
alg.after = after;
return alg;
}
public void run () {
Object key = null;
if (before != null) key = before.get();
test(algClass.getSimpleName() + (key == null ? "" : key), (source, target) -> {
try {
return (Set<Word>) algClass.getMethod("check", String.class, String.class).invoke(null, source, target);
} catch (Exception e) {
System.out.println(algClass.getSimpleName() + " 调用方法失败:" + e.getMessage());
}
return null;
});
if (after != null) after.run();
}
}
public static void test (String algName, BiFunction<String, String, Set<Word>> task) {
String target = "畜生";
Set<Word> words = task.apply("好好的人不做要做畜生,尽干些畜生的事", target);
System.out.println(algName + ":" + words);
words = task.apply("畜生就是畜生", target);
System.out.println(algName + ":" + words);
words = task.apply("畜生就不要妄想人的事,做一生的生畜", target);
System.out.println(algName + ":" + words);
words = task.apply("GTGTGAGTGAGTGT", "GTGAGT");
System.out.println(algName + ":" + words);
words = task.apply("AGGAGAGAGAGA", "GAGAGA");
System.out.println(algName + ":" + words);
}
public static class KMP {
public static Set<Word> check (String source, String target) {
assert !(isBlank(source) || isBlank(target)) && source.length() >= target.length();
int n = source.length(), m = target.length();
int[] next = buildKMPNext(target);
Set<Word> result = new HashSet<>();
int offset = 0, index = 0;
for (; index < n; index++) {
while(offset > 0 && target.charAt(offset) != source.charAt(index)) {
offset = next[offset];
}
if (target.charAt(offset) == source.charAt(index)) {
offset++;
}
if (offset == m) {
result.add(Word.of(source.substring(index - m + 1, index + 1), index - m + 1));
offset = 0;
}
}
return result;
}
private static int[] buildKMPNext (String pattern) {
assert !isBlank(pattern);
int[] next = new int[pattern.length()];
int offset = 0, index = 2;
for (; index < next.length; index++) {
while(offset > 0 && pattern.charAt(offset) != pattern.charAt(index - 1)) {
offset = next[offset];
}
if (pattern.charAt(offset) == pattern.charAt(index - 1)) {
offset++;
}
next[index] = offset;
}
return next;
}
}
public static class Sunday {
public static Set<Word> check (String source, String target) {
assert !(isBlank(source) || isBlank(target)) && source.length() >= target.length();
int n = source.length(), m = target.length();
Set<Word> result = new HashSet<>();
int start = 0, end = start + m - 1, offset;
while (end < n) {
offset = 0;
while (offset < m && source.charAt(start + offset) == target.charAt(offset)) ++offset;
if (offset == m) {
result.add(Word.of(source.substring(start, start + m), start));
start = end + 1;
} else {
if(end == n - 1) {
break;
}
offset = m - 1;
while (offset >= 0 && source.charAt(end + 1) != target.charAt(offset)) --offset;
if (offset >= 0) {
start += m - offset;
} else {
start = end + 1;
}
}
end = start + m - 1;
}
return result;
}
}
public static class BM {
public static int type = -1;
public static Set<Word> check (String source, String target) {
return type == 0 ? bm_bad(source, target) : type == 1 ? bm_good(source, target) : bm_mix(source, target);
}
private static Set<Word> bm_mix (String source, String target) {
assert !(isBlank(source) || isBlank(target)) && source.length() >= target.length();
int n = source.length(), m = target.length();
int[] suffix = new int[m - 1];
boolean[] prefix = new boolean[m - 1];
buildSuffixOfBMGood(target, suffix, prefix);
Set<Word> result = new HashSet<>();
int start = 0, offset;
while (start <= n - m) {
offset = m - 1;
while (offset >= 0 && source.charAt(start + offset) == target.charAt(offset)) --offset;
if (offset < 0) {
result.add(Word.of(source.substring(start, start + m), start));
start += m;
continue;
}
int badStep, goodStep = 0, badIndex = offset;
offset = m - 1;
while (offset >= 0 && target.charAt(badIndex) == target.charAt(offset)) --offset;
badStep = badIndex - offset;
if (badIndex < m - 1) {
goodStep = nextStepOfBMGood(badIndex, m, suffix, prefix);
}
start += Math.max(badStep, goodStep);
}
return result;
}
private static Set<Word> bm_bad (String source, String target) {
assert !(isBlank(source) || isBlank(target)) && source.length() >= target.length();
int n = source.length(), m = target.length();
Set<Word> result = new HashSet<>();
int start = 0, offset;
while (start >= 0 && start <= n - m) {
offset = m - 1;
while (offset >= 0 && source.charAt(start + offset) == target.charAt(offset)) --offset;
if (offset < 0) {
result.add(Word.of(source.substring(start, start + m), start));
start += m;
continue;
}
int badIndex = offset;
while (offset >= 0 && source.charAt(start + badIndex) != target.charAt(offset)) --offset;
if (offset < 0) {
start += m;
continue;
}
start += badIndex - offset;
}
return result;
}
private static Set<Word> bm_good (String source, String target) {
assert !(isBlank(source) || isBlank(target)) && source.length() >= target.length();
int n = source.length(), m = target.length();
int[] suffix = new int[m - 1];
boolean[] prefix = new boolean[m - 1];
buildSuffixOfBMGood(target, suffix, prefix);
Set<Word> result = new HashSet<>();
int start = 0, offset;
while (start <= n - m) {
offset = m - 1;
while (offset >= 0 && source.charAt(start + offset) == target.charAt(offset)) --offset;
if (offset == m - 1) {
start += 1;
continue;
}
if (offset < 0) {
result.add(Word.of(source.substring(start, start + m), start));
start += m;
continue;
}
start += nextStepOfBMGood(offset, m, suffix, prefix);
}
return result;
}
private static int nextStepOfBMGood (int badIndex, int m, int[] suffix, boolean[] prefix) {
assert badIndex >= 0 && badIndex < m - 1;
int lenIndex = m - (badIndex + 1) - 1;
if (suffix[lenIndex] != -1) {
return (badIndex + 1) - suffix[lenIndex];
}
for (int i = badIndex + 2; i < m; i++) {
if (prefix[m - i - 1]) {
return i;
}
}
return m;
}
private static void buildSuffixOfBMGood (String pattern, int[] suffix, boolean[] prefix) {
assert !isBlank(pattern);
int len = pattern.length();
for (int i = 0; i < len - 1; i++) {
suffix[i] = -1;
}
for (int i = 0; i < len - 1; i++) {
int offset = i
, lenIndex = 0;
while (offset >= 0 && pattern.charAt(offset) == pattern.charAt(len - 1 - lenIndex)) {
suffix[lenIndex] = offset;
++lenIndex;
--offset;
}
if (offset == -1) {
prefix[lenIndex - 1] = true;
}
}
}
}
public static class RK {
public static Set<Word> check (String source, String target) {
assert !(isBlank(source) || isBlank(target)) && source.length() >= target.length();
int n = source.length(), m = target.length();
int i;
int[] th = new int[]{0};
hash(th, target, 0, m);
int[] sh = new int[n - m + 1];
for (i = 0; i <= n - m; i++) {
hash(sh, source, i, m);
}
Set<Word> result = new HashSet<>();
for (i = 0; i < sh.length; i++) {
if (sh[i] == th[0]) {
result.add(Word.of(source.substring(i, i + m), i));
}
}
return result;
}
private static void hash (int[] h, String str, int si, int len) {
assert !isBlank(str) && si >= 0 && len > 0 && si <= str.length() - len;
if (si == 0) {
h[0] = 0;
for (int i = 0; i < len; i++) {
h[0] += calc(str.charAt(i), len - i - 1);
}
} else {
h[si] = h[si - 1] - calc(str.charAt(si-1), len - 1) + calc(str.charAt(si+len-1), 0);
}
}
private static int calc (Character c, int exp) {
assert c != null && exp >= 0;
return (c-'a');
}
}
public static class BF {
public static Set<Word> check (String source, String target) {
assert !(isBlank(source) || isBlank(target)) && source.length() >= target.length();
int n = source.length(), m = target.length();
Set<Word> result = new HashSet<>();
sl:for (int i = 0; i <= n - m; i++) {
for (int j = 0; j < m; j++) {
if(source.charAt(i+j) != target.charAt(j)) continue sl;
}
result.add(Word.of(target, i));
}
return result;
}
}
public static boolean isBlank (String str) {
return str == null || str.trim().isEmpty();
}
public static class Word {
private int index;
private String key;
public static Word of (String key, int index) {
assert key != null && index >= 0;
Word word = new Word();
word.index = index;
word.key = key;
return word;
}
public int getIndex () { return index; }
public String getKey () { return key; }
public String toString () {
return "{\"key\":\"" + key + "\", \"index\":" + index + "}";
}
}
}