Sunday 算法
介绍
1、Sunday 算法是 BM 算法的改进,Sunday算法的位移比BM算法更大,在匹配随机字符串时效率比其他匹配算法快
2、时间复杂度:文本串长度为 n,模式串长度为 m
(1)最好 O(n / m)
(2)平均 O(n)
(3)最坏 O(n * m)
规则
1、从前往后匹配,在匹配失败时关注的是文本串中参加匹配的最末位字符的下一位字符
2、如果该字符没有在模式串中出现则直接跳过,即后移位数 = 匹配串长度 + 1
3、如果该字符在模式串中出现,后移位数 = 模式串中最右端的该字符到末尾的距离 + 1,使得文本串参与匹配的最末尾字符的下一位字符,与模式串最右端的该字符相匹配
代码实现
1、根据模式串的约束,对模式串进行预处理,提前建立字符失配时,所对应的移动距离,提高查找效率
public class Sunday {
private static final int MAX_SIZE = 65536;//数组容量可变,依字符范围而定
private static final int[] MOVE_LENGTH = new int[MAX_SIZE];//匹配失败时的移动距离
//设置移动距离
private static void setMoveLength(int targetLength, String target) {
int plus = targetLength + 1;
for (int i = 0; i < MAX_SIZE; i++) {//默认子串中的任何字符不出现在母串中,移动距离是子串长度 + 1
MOVE_LENGTH[i] = plus;
}
for (int i = 0; i < targetLength; i++) {//确定母串匹配部分最后一个字符的下一个字符在子串中最右出现的位置
//默认遍历到的元素就是target中最后一个相同元素,最后一个相同元素会覆盖前面的值,所以前面相同元素不会造成影响
//模式串中最右端的该字符到末尾的距离 + 1 = (末尾下标 - 最右字符下标i) + 1 = target长度 - 最右字符下标i
MOVE_LENGTH[target.charAt(i)] = targetLength - i;
}
}
//顺序查找指定子串在指定母串中首次出现的位置
public static int sundayMatch(String source, String target) {
setMoveLength(target.length(), target);//设置移动距离
int i = 0;//i是母串遍历下标
while (i < source.length()) {
int j = 0;//j是子串遍历下标
while (j < target.length() && i + j < source.length() && source.charAt(i + j) == target.charAt(j)) {
j++;
}
if (j == target.length()) {//匹配成功
return i;
}
//下标越界(或剩下的元素超出target元素个数:i + 1 + target.length() > source.length())
if (i + target.length() > source.length() - 1) {
return -1;
}
i += MOVE_LENGTH[source.charAt(i + target.length())];
}
return -1;
}
}
2、不对模式串预处理,直接匹配
public class Sunday {
public int sundayMatch(String haystack, String needle) {
int hayLen = haystack.length();
int nLen = needle.length();
int i = 0;//haystack串的游标索引
int j = 0;// needle串的游标索引
// haystack剩余字符少于needle串时跳过比较
// 从0到i的字符串长度为i+1(包含 i),因为i下标的字符未进行比较,所以剩余未比较字符数 = hayLen - (i + 1 - 1)
while (nLen <= hayLen - i) {
// 将needle串与haystack串中参与比较的子串进行逐个字符比对
while (j < nLen && haystack.charAt(i + j) == needle.charAt(j)) {
j++;
}
// 如果j等于needle串的长度说明此时匹配成功,可以直接返回此时主串的游标索引
if (j == nLen) {
return i;
}
// 不匹配时计算需要跳过的字符数,移动主串游标i
// haystack剩余字符少于needle串时,i 不需要增加步长
// 从0到i的字符串长度为i+1(包含 i),因为i下标的字符已经比较,所以剩余未比较字符数 = hayLen - (i + 1)
if (nLen <= hayLen - (i + 1)) {
// 对照字符在needle串存在,则需要跳过的字符数为从对照字符在needle串中最后出现的位置起剩余的字符个数
// 不存在则跳过的字符数为needle串长度+1,也就是 nLen - (-1) 的情况
i += (nLen - lastIndex(needle, haystack.charAt(i + nLen)));
} else {
return -1;
}
// 每次比较之后将needle游标置为0
j = 0;
}
return -1;
}
public int lastIndex(String needle, char ch) {
// 从后往前检索,查找最右端字符的下标
for (int j = needle.length() - 1; j >= 0; j--) {
if (needle.charAt(j) == ch) {
return j;
}
}
return -1;
}
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战