字符串匹配算法
Posted jiezao
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了字符串匹配算法相关的知识,希望对你有一定的参考价值。
1.1BF算法
其实就是暴力解法,直接双重循环,干就完事了。虽然算不上什么好方法,但是非常简单。对于所有的暴力算法,我们应该思考如何进行优化,比如BF算法,当我们遇到不匹配字符的时候,只能从头的下一个字符开始匹配。这样其实做了很多无用的重复工作。那么我们可以怎样优化呢?下面介绍两种。二者的思想都是避免无用功的移动。
1.2BM算法
BM算法是从匹配串的后往前匹配。遇到不匹配的字符时,将被匹配串向后移动到匹配字符或者移动到匹配串头。然后再从后往前匹配。
字符串与搜索词头部对齐,从尾部字符开始比较:
这里右移长度有一个规则一:step_bad = pos1 - pos2
step_bad表示向后移动位数;pos1表示坏字符的位置;pos2表示坏字符在搜索词中出现的最右位置(如果不存在就取值为-1)
单是这一条规则并不能保证移动后移长度最合适,我们还需要加一条好后缀规则:step_good = pos1 - pos2
step_good为后移位数;pos1表示好后缀的位置;pos2表示好后缀在搜索词中剩余部分出现的最右位置;
我们发现step_bad 为3 step_good为6,所以我们右移6位。
直到最后得出结果
- 代码
package matchstring;
import java.util.Arrays;
/**
* JavaTest
*
* @author : xgj
* @description : BM算法
* @date : 2020-07-31 22:40
**/
public class BM {
public static int pattern(String target , String pattern) {
int tLen = target.length();
int pLen = pattern.length();
if (pLen > tLen) {
return -1;
}
int[] badTable = buildBadTable(pattern);
int[] goodTable = buildGoodTable(pattern);
for (int i = pLen - 1, j; i < tLen; ) {
System.out.println("跳跃位置:" + i);
for (j = pLen - 1; target.charAt(i) == pattern.charAt(j); i--, j--) {
if (j == 0) {
System.out.println("匹配成功,位置:" + i);
return i;
}
}
i += Math.max(goodTable[pLen - j - 1], badTable[target.charAt(i)]);
}
return -1;
}
/**
* 字符信息表
*/
public static int[] buildBadTable(String pattern) {
final int tableSize = 256;
int[] badTable = new int[tableSize];
int pLen = pattern.length();
Arrays.fill(badTable, pLen);
for (int i = 0; i < pLen - 1; i++) {
int k = pattern.charAt(i);
badTable[k] = pLen - 1 - i;
}
return badTable;
}
/**
* 匹配偏移表。
*
* @param pattern 模式串
* @return
*/
public static int[] buildGoodTable(String pattern) {
int pLen = pattern.length();
int[] goodTable = new int[pLen];
int lastPrefixPosition = pLen;
for (int i = pLen - 1; i >= 0; --i) {
if (isPrefix(pattern, i + 1)) {
lastPrefixPosition = i + 1;
}
goodTable[pLen - 1 - i] = lastPrefixPosition - i + pLen - 1;
}
for (int i = 0; i < pLen - 1; ++i) {
int glen = suffixLength(pattern, i);
goodTable[glen] = pLen - 1 - i + glen;
}
return goodTable;
}
/**
* 前缀匹配
*/
private static boolean isPrefix(String pattern, int p) {
int patternLength = pattern.length();
for (int i = p, j = 0; i < patternLength; ++i, ++j) {
if (pattern.charAt(i) != pattern.charAt(j)) {
return false;
}
}
return true;
}
/**
* 后缀匹配
*/
private static int suffixLength(String pattern, int p) {
int pLen = pattern.length();
int len = 0;
for (int i = p, j = pLen - 1; i >= 0 && pattern.charAt(i) == pattern.charAt(j); i--, j--) {
len += 1;
}
return len;
}
}
1.3KMP算法
BM是以被匹配串尾部为基准的话,KMP算法就是以被匹配串头部为基准。相对于而言KMP算法更加接近人的思考方式。
对于暴力算法,在出现不匹配的时候,我们是一位一位的往右移的。但是实际上要是人手工来做的话,会容易发现有时候可以一次移动多位。例如“SSSSSSSSSSSSSA”中查找“SSSSB”。要是人眼比较对于前面的s移动时,会一次性移动到倒数第二位,而不是一位一位的移动。那么在计算机中我们怎么实现聪明的移动呢?“利用已经部分匹配这个有效信息,保持i指针不回溯,通过修改j指针,让模式串尽量地移动到有效的位置”
- 代码
package matchstring;
/**
* JavaTest
*
* @author : xgj
* @description : KMP算法
* @date : 2020-07-31 22:24
**/
public class KMP {
/**
*功能描述 : 匹配字符串
*
* @author xgj
* @date 2020/7/31
* @param s
* @param t
* @return boolean
*/
public static boolean matchString(String s, String t) {
int[] next = getNext(t);
char[] sArr = s.toCharArray();
char[] tArr = t.toCharArray();
int i = 0, j = 0;
while (i < sArr.length && j < tArr.length) {
if (j == -1 || sArr[i] == tArr[j]) {
i++;
j++;
} else {
j = next[j];
}
}
return j == tArr.length;
}
/**
*功能描述 : 返回next数组
*
* @author xgj
* @date 2020/7/31
* @param ps
* @return int[]
*/
public static int[] getNext(String ps) {
char[] p = ps.toCharArray();
int[] indexArray = new int[p.length];
indexArray[0] = -1;
int j = 0;
int k = -1;
while (j < p.length - 1) {
if (k == -1 || p[j] == p[k]) {
if (p[++j] == p[++k]) {
indexArray[j] = indexArray[k];
} else {
indexArray[j] = k;
}
} else {
k = indexArray[k];
}
}
return indexArray;
}
}
以上是关于字符串匹配算法的主要内容,如果未能解决你的问题,请参考以下文章