如何在Java中实现字符串匹配函数
字符串匹配可以是在一个文本串中查找一个模式串出现的位置或是否存在的问题。在Java中实现字符串匹配函数有很多方法,本文介绍几种主要的实现方法。
1.朴素的字符串匹配算法
朴素算法常常被称为暴力匹配算法或者叫 Brute Force,是最基本的字符串匹配算法。它的原理就是逐个比较文本串中每一个可能的位置,判断是否与模式串完全相等。
代码实现:
public static int naiveStringMatch(String text, String pattern) {
int n = text.length();
int m = pattern.length();
for (int i = 0; i <= n-m; i++) {
int j;
for (j = 0; j < m; j++) {
if (text.charAt(i+j) != pattern.charAt(j)) {
break;
}
}
if (j == m) {
return i;
}
}
return -1;
}
分析:该算法时间复杂度为 $O(nm)$,其中 n 和 m 分别是文本串和模式串的长度。在最坏的情况下,需要比较的次数是 $(n-m+1)\times m$。因此,朴素算法并不适合处理大规模的文本串。
2. KMP算法
KMP算法,全称为 Knuth-Morris-Pratt 算法,是一个高效的字符串匹配算法。它利用了模式串本身的某些特点,并预处理出一个 next 数组,来决定模式串与文本串的比较顺序,从而提高匹配的效率。
代码实现:
public static int KMPSearch(String text, String pattern) {
int n = text.length();
int m = pattern.length();
int[] next = getNext(pattern);
int i = 0, j = 0;
while (i < n && j < m) {
if (j == -1 || text.charAt(i) == pattern.charAt(j)) {
i++;
j++;
} else {
j = next[j];
}
}
if (j == m) {
return i - j;
} else {
return -1;
}
}
private static int[] getNext(String pattern) {
int m = pattern.length();
int[] next = new int[m];
next[0] = -1;
int i = 0, j = -1;
while (i < m - 1) {
if (j == -1 || pattern.charAt(i) == pattern.charAt(j)) {
next[++i] = ++j;
} else {
j = next[j];
}
}
return next;
}
分析:该算法时间复杂度为 $O(n+m)$,其中 n 和 m 分别是文本串和模式串的长度。KMP 算法利用 next 数组的信息,消除了朴素算法的重复比较过程,从而达到了提高效率的目的。
3. Boyer-Moore算法
Boyer-Moore 算法是一种常用的字符串匹配算法,其主要思想是从模式串尾部向前匹配,并充分利用“坏字符规则”和“好后缀规则”来快速跳过不可能匹配的情况。
代码实现:
public static int BM(String text, String pattern) {
int n = text.length();
int m = pattern.length();
int[] bc = generateBC(pattern);
int[] suffix = new int[m];
boolean[] prefix = new boolean[m];
generateSuffixAndPrefix(pattern, suffix, prefix);
int i = 0;
while (i <= n - m) {
int j;
for (j = m - 1; j >= 0; j--) {
if (text.charAt(i+j) != pattern.charAt(j)) {
break;
}
}
if (j < 0) {
return i;
}
int x = j - bc[text.charAt(i+j)];
int y = 0;
if (j < m - 1) {
y = moveByGS(j, m, suffix, prefix);
}
i = i + Math.max(x, y);
}
return -1;
}
private static int[] generateBC(String pattern) {
int[] bc = new int[256];
for (int i = 0; i < 256; i++) {
bc[i] = -1;
}
for (int i = 0; i < pattern.length(); i++) {
int ascii = pattern.charAt(i);
bc[ascii] = i;
}
return bc;
}
private static void generateSuffixAndPrefix(String pattern, int[] suffix, boolean[] prefix) {
int m = pattern.length();
for (int i = 0; i < m; i++) {
suffix[i] = -1;
prefix[i] = false;
}
for (int i = 0; i < m - 1; i++) {
int j = i;
int k = 0;
while (j >= 0 && pattern.charAt(j) == pattern.charAt(m-1-k)) {
j--;
k++;
suffix[k] = j + 1;
}
if (j == -1) {
prefix[k] = true;
}
}
}
private static int moveByGS(int j, int m, int[] suffix, boolean[] prefix) {
int k = m - 1 - j;
if (suffix[k] != -1) {
return j - suffix[k] + 1;
}
for (int r = j + 2; r <= m - 1; r++) {
if (prefix[m-r]) {
return r;
}
}
return m;
}
分析:该算法时间复杂度为 $O(nm)$,但其实际性能比朴素算法和 KMP 算法好。Boyer-Moore 算法通过实现坏字符规则和好后缀规则,大大减少了需要比较字符的数目。
总结
以上就是几种常用的字符串匹配算法的实现。各算法的优缺点不同,根据实际情况来选择适合的算法。KMP 算法一般情况下最为实用,Boyer-Moore 算法在某些情况下效率更高。
