串的模式匹配算法
尴尬啊,大学数据结构课程用的书看到了现在。说起来这本书在业内也是相当有名,它就是清华大学出版社出版的《数据结构(C语言版)》。在该书的 4.3 节,讲解了“串的模式匹配算法”,个人感觉这一节应该是前四章最值得看的一节了。下面就说说相关算法的爪哇实现吧!
简单粗暴
首先,肯定还是说最简单最暴力的方法喽,没废话就是从前向后遍历:
/**
* Cerated by clearbug on 2018/2/23.
*
* 串的模式匹配算法
*/
public class SubStrMatcher {
public static void main(String[] args) {
System.out.println(indexForce("ababcabcacbab", "abcac", 5));
System.out.println(indexForce("00000000000000000000000000000000000000000000000001", "0000000001", 0));
}
/**
* 朴素字符串匹配算法
*
* @param s 主串
* @param t 模式串
* @param pos 匹配操作起始位置
* @return 若模式串匹配不成功,则返回 -1;否则,返回主串中第一次匹配成功的子串的索引值;
*/
public static int indexForce(String s, String t, int pos) {
char[] sArr = s.toCharArray();
char[] tArr = t.toCharArray();
int sLen = sArr.length;
int tLen = tArr.length;
int i = pos, j = 0;
while (i < sLen && j < tLen) {
if (sArr[i] == tArr[j]) {
i++;
j++;
} else {
i = i -j + 1;
j = 0;
}
}
if (j == tLen) { // 匹配成功了
return i - j;
}
return -1;
}
}
后来,我听说这种算法也是有个优雅的名字的—BF算法,全称并不是美好的“Boy Friend”,而是更美好的“Brute Force”!
优雅点
优雅点的当然就是大名鼎鼎的 KMP 算法了,为啥要叫它 KMP 算法呢?因为它是由 D.E.Knuth 与 V.R.Pratt 和 J.H.Morris 这三位大佬同时发现的,所以简称为 KMP 算法,俗称为“看毛片”算法。
这个算法虽然思路简单,但是实现却是好复杂哦,像我这种算法渣渣看了一天的博客教程+视频教程才稍微写出了其实现。哎,上帝限制了我的智商!下面就直接贴代码实现吧:
/**
* 原始 KMP 算法实现
*
* @param s 主串
* @param t 模式串
* @param pos 匹配操作起始位置
* @return 若模式串匹配不成功,则返回 -1;否则,返回主串中第一次匹配成功的子串的索引值;
*/
public static int indexKmp(String s, String t, int pos) {
char[] sArr = s.toCharArray();
char[] tArr = t.toCharArray();
int[] next = kmpNext(t);
int sLen = sArr.length;
int tLen = tArr.length;
int i = pos, j = 0;
while (i < sLen && j < tLen) {
if (sArr[i] == tArr[j]) {
i++;
j++;
} else {
if (next[j] == -1) {
i++;
j = 0;
} else {
j = next[j];
}
}
}
if (j == tLen) {
return i - j;
}
return -1;
}
/**
* 原始 KMP 算法的 next 方法实现
*
* @param t 模式字符串
* @return next 数组
*/
public static int[] kmpNext(String t) {
char[] tArr = t.toCharArray();
int[] next = new int[tArr.length];
next[0] = -1;
int i = -1, j = 0; // i:前缀;j:后缀;
while (j < tArr.length - 1) {
if (i == -1 || tArr[i] == tArr[j]) {
i++;
j++;
next[j] = i;
} else {
i = next[i];
}
}
return next;
}
再优雅一点
后人在使用 KMP 算法的同时也没有忘记优化它,然后就出来了一个优化后的 next 方法实现:
/**
* 优化后的 KMP 算法的 next 方法实现
*
* @param t 模式字符串
* @return next 数组
*/
public static int[] optdKmpNext(String t) {
char[] tArr = t.toCharArray();
int[] next = new int[tArr.length];
next[0] = -1;
int i = -1, j = 0; // i:前缀;j:后缀;
while (j < tArr.length - 1) {
if (i == -1 || tArr[i] == tArr[j]) {
i++;
j++;
if (tArr[i] == tArr[j]) {
next[j] = next[i];
} else {
next[j] = i;
}
} else {
i = next[i];
}
}
return next;
}
再度进化-Horspool算法
参考博客里面我看作者提到了这个 Horspool 算法,于是我也试着写了下它的实现:
public static int indexHorspool(String s, String t, int pos) {
char[] sArr = s.toCharArray();
char[] tArr = t.toCharArray();
int sLen = sArr.length;
int tLen = tArr.length;
int i = pos, j = tLen - 1;
while (i + j < sLen && j >= 0) {
if (sArr[i + j] == tArr[j]) {
j--;
} else {
int k = j - 1;
while (k >= 0) {
if (tArr[k] == sArr[i + j]) {
i = i + (j - k);
break;
}
k--;
}
if (k == -1) {
i = i + j;
}
j = tLen - 1;
}
}
if (j == -1) {
return i;
}
return -1;
}
超级进化-BM算法
到了这里我已经完全看不懂了,看了不少博客视频教程都把我看傻逼了。暂时先把找到的源码贴在这里吧:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
/**
* Cerated by clearbug on 2018/2/25.
*/
public class BoyerMoore {
/** function findPattern **/
public void findPattern(String t, String p) {
char[] text = t.toCharArray();
char[] pattern = p.toCharArray();
int pos = indexOf(text, pattern);
if (pos == -1) {
System.out.println("\nNo Match\n");
} else {
System.out.println("Pattern found at position: " + pos);
}
}
/** Function to calculate index of pattern substring **/
public int indexOf(char[] text, char[] pattern) {
if (pattern.length == 0) {
return 0;
}
int[] charTable = makeCharTable(pattern);
int[] offsetTable = makeOffsetTable(pattern);
for (int i = pattern.length - 1, j; i < text.length; ) {
for (j = pattern.length - 1; pattern[j] == text[i]; --i, --j) {
if (j == 0) {
return i;
}
}
i = i + Math.max(offsetTable[pattern.length - 1 -j], charTable[text[i]]);
}
return -1;
}
/** Makes the jump table based on the mismatched character information **/
private int[] makeCharTable(char[] pattern) {
final int ALPHABET_SIZE = 256;
int[] table = new int[ALPHABET_SIZE];
for (int i = 0; i < table.length; i++) {
table[i] = pattern.length;
}
for (int i = 0; i < pattern.length - 1; i++) {
table[pattern[i]] = pattern.length - 1 - i;
}
return table;
}
/** Makes the jump table based on the scan offset which mismatch occurs. **/
private static int[] makeOffsetTable(char[] pattern) {
int[] table = new int[pattern.length];
int lastPrefixPosition = pattern.length;
for (int i = pattern.length - 1; i >= 0; i--) {
if (isPrefix(pattern, i + 1)) {
lastPrefixPosition = i + 1;
}
table[pattern.length - 1 - i] = lastPrefixPosition - i + pattern.length - 1;
}
for (int i = 0; i < pattern.length - 1; i++) {
int slen = suffixLength(pattern, i);
table[slen] = pattern.length - 1 - i + slen;
}
return table;
}
/** function to check if needle[p:end] a prefix of pattern **/
private static boolean isPrefix(char[] pattern, int p) {
for (int i = p, j = 0; i < pattern.length; i++, j++) {
if (pattern[i] != pattern[j]) {
return false;
}
}
return true;
}
/** function to returns the maximum length of the substring ends at p and is a suffix **/
private static int suffixLength(char[] pattern, int p) {
int len = 0;
for (int i = p, j = pattern.length - 1; i >= 0 && pattern[i] == pattern[j]; i--, j--) {
len = len + 1;
}
return 0;
}
/** Main Function **/
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.println("Boyer Moore Algorithm Test");
System.out.println("Enter Text\n");
// String text = br.readLine();
String text = "abcdefghijklmnopqrstuvwxyz";
System.out.println("Enter Pattern\n");
// String pattern = br.readLine();
String pattern = "qrstuv";
BoyerMoore bm = new BoyerMoore();
bm.findPattern(text, pattern);
}
}