在进行搜索的时候,经常要使用到字符串匹配算法,下面总结几种字符串匹配的算法,以C#代码为例
1、BF匹配算法
最简单的匹配算法,时间复杂度为O(m*n),原理:逐个匹配,若发现不匹配,则后移一位继续匹配,
从pos位置开始,在 source 中找出与 target 匹配的子串的位置,若未找到,返回-1
//BF匹配算法,时间复杂度O(m*n) private int BFIndex(string source, string target, int pos) { int i = pos, j = 0; while (i + j < source.Length && j < target.Length) { if (source[i + j] == target[j]) { j++; } else { i++; j = 0; } } if (j == target.Length) { return i; } else { return -1; } }
2、KMP算法
KMP算法在BF算法的基础上做了改进,减少回溯,时间复杂度为O(m+n)
首先计算出所有target所有字符的新起点,以减少字符串在不匹配的情况下回退的字符
下面看代码,如果不能理解的话,把代码抄下来,多分析几遍,就会懂了
private int[] GetNext(string target) { int k = -1; int i = 0; int[] next = new int[target.Length]; next[0] = -1; while (i < target.Length - 1) { if (k == -1 || target[k] == target[i]) { k = k + 1; i = i + 1; next[i] = k; } else { k = next[k]; } } return next; } //KMP匹配算法,时间复杂度为O(m+n) private int KMPIndex(string source, string target, int pos) { int[] next = GetNext(target); int i = pos, j = 0; while (i < source.Length && j < target.Length) { if (j == -1 || source[i] == target[j]) { i = i + 1; j = j + 1; } else { j = next[j]; } } if (j >= target.Length) { return (i - target.Length); } else { return (-1); } }
3、Horspool匹配算法
Horspool算法是后缀匹配算法,从最后一个字符向左匹配,然后进行安全移动
下面是代码
//Horspool匹配算法,平均时间复杂度O(n) private int HorspoolIndex(string source, string target, int pos) { int sLen = source.Length; int tLen = target.Length; int Ti = tLen - 1; //目标字符串匹配位置 int Si = pos + Ti; //源字符串匹配位置 if ((sLen - pos) < tLen) return -1; while ((Ti > -1) && (Si < sLen)) { if (source[Si] == target[Ti]) { Ti--; Si--; } else { while ((Ti > -1) && (source[Si] != target[Ti])) { Ti--; } Si += tLen - 1 - Ti; Ti = tLen - 1; } } if (Si < sLen) return(Si + 1); else return -1; }
4、BM匹配算法
BM算法在Horspool算法的基础上结合了KMP算法,从而算出最大右移位置,基本思路与Horspool算法相似
思路:
从右往左匹配
匹配失败后,计算出最大右移位置,然后向右移动,继续匹配
两个规则
坏字符算法:思想与Horspool差不多,看的懂Horspool的一般不难理解
好后缀算法:计算目标字符串是否含有已匹配的后缀suffix,如果没有,计算出好后缀(目标字符串中的前缀和后缀一样),然后右移对齐字符串
废话不多说,下面图解
代码如下:
//BM匹配算法,最好情况下的时间复杂度为O(n/m),最坏情况下时间复杂度为O(m·n) private int BMIndex(string source, string target, int pos) { int i = target.Length - 1; int j = i; while ((j > -1) && i < source.Length) { if (source[i] == target[j]) { i--; j--; } else { int moveStep = Math.Max(BadCharacter(target, j, source[i]), GoodSuffix(target, j)); i += moveStep; } } if (j == -1) { return i + 1; } return -1; } //返回目标字符串右移位数 private int BadCharacter(string target, int pos, char ch) { int j = pos - 1; while (j > -1) { if (target[j] == ch) { return pos - j; } j--; } return pos; } //返回目标字符串右移位数 private int GoodSuffix(string target, int pos) { string suffix = target.Substring(pos + 1, target.Length - pos - 1); //找子串 int a = target.Substring(0, target.Length - 1).LastIndexOf(suffix); if (a > -1) { return pos - a; } else { //找后缀 return FindSuffix(target, pos); } } //找出前缀与后缀相同的字符,计算出移动位数 private int FindSuffix(string target, int pos) { int i = 0; int ppos = pos + 1; int j = ppos; while (j < target.Length) { if (target[i] == target[j]) { i++; j++; } else { i = 0; j = ppos = ppos + 1; } } if (i == 0) { return -1; } else { return ppos; } }
5、Sunday匹配算法
//Sunday匹配算法 private int SundayIndex(string source, string target) { int i, j, p; i = j = p = 0; while (i <= source.Length - target.Length && j < target.Length) { if (source[p] == target[j]) { p++; j++; } else { int k = target.Length; while (k > 0) { k--; if (target[k] == source[i + target.Length]) break; } i += target.Length - k; p = i; j = 0; } } if (j == target.Length) { return i; } return -1; }
..