Sunday算法由Daniel M.Sunday在1990年提出,它的思想跟BM算法很相似:
Sunday算法 算法是比BM 和KBM更高效的算法,它不关注好后缀与坏字符,只关注文本串中参加匹配的最末位字符的下一位字符的情况
Sunday算法是从前往后匹配,在匹配失败时关注的是文本串中参加匹配的最末位字符的下一位字符。
- 1 如果该字符没有在模式串中出现则直接跳过,即移动位数 = 匹配串长度 + 1;
- 2 否则,其移动位数 = 模式串中最右端的该字符到末尾的距离+1
1 2 可以直接概括为模式串.length - 模式串.lastIndexOf©
举例说明
假定现在要在文本串"substring searching algorithm"中查找模式串"search"。
1 刚开始时,把模式串与文本串左边对齐:
substring searching algorithm
search
^
2. 结果发现在第2个字符处发现不匹配,不匹配时关注文本串中参加匹配的最末位字符的下一位字符,即i,因为模式串search中并不存在i,所以模式串直接跳过一大片,向右移动位数 = 匹配串长度 + 1 = 6 + 1 = 7,从 i 之后的那个字符(即字符n)开始下一步的匹配,如下:
substring searching algorithm
search
^
3. 结果第一个字符就不匹配,再看文本串中参加匹配的最末位字符的下一位字符,是’r’,它出现在模式串中的倒数第3位,于是把模式串向右移动3位(r 到模式串末尾的距离 + 1 = 2 + 1 =3),使两个’r’对齐,比配成功
substring searching algorithm
search
^
Sunday及其高效和简单,代码实现也非常简单
和BM算法一样记录参与文本串匹配的最末尾字符的下一个字符,记录其在模式串出现的位置
private static HashMap<Character,Integer> getMatchIndexMap(String patternString){
int j = 0;
HashMap<Character,Integer> matchMap = new HashMap<>();
//注意需要的是上一次出现的位置,即最后次出现的索引位置,如果有相同的字符则取最后一个的索引位置
while (j < patternString.length()-1){
int finalJ = j;
matchMap.compute(patternString.charAt(j++) ,(k, v)-> finalJ);
}
return matchMap;
}
总体代码简单,逻辑简单,思想简单,对于字符串匹配来说真是个非常不错的算法。
public static int sundayMatch (String textString, String patternString){
if ( textString.length() < patternString.length()) return -1;
int i = 0,j = 0;
int patternLen = patternString.length();
int textLen = textString.length();
HashMap<Character, Integer> matchMap = getMatchIndexMap(patternString);
while (i < textLen ){
if (textString.charAt(i) == patternString.charAt(j)){
i++;
j++;
}
else {
char c = textString.charAt(patternLen-j+i);//文本串中参加匹配的最末位字符的下一位字符
i = i -j + patternLen - matchMap.getOrDefault(c,-1);//移动距离
j = 0;
}
if (j == patternLen) {
return i-j ;
}
}
return -1;
}