第一次听到Sunday算法,是大饼饼说的。在他图文并茂的解释中,我发现这个算法果然是一个又容易理解,效率又强过kmp和BM的算法。
Sunday的移动次数更少!
于是试着写了一个,果真是好东东,分享一下。
转一些概念先:
Sunday算法是Daniel M.Sunday于1990年提出的一种比BM算法搜索速度更快的算法。其核心思想是:在匹配过程中,模式串并不被要求一定要按从左向右进行比较还是从右向左进行比较,它在发现不匹配时,算法能跳过尽可能多的字符以进行下一步的匹配,从而提高了匹配效率。
假设在发生不匹配时S[i]≠T[j],1≤i≤N,1≤j≤M。此时已经匹配的部分为u,并假设字符串u的长度为L。如图1。明显的,S[L+i+1]肯定要参加下一轮的匹配,并且T[M]至少要移动到这个位置(即模式串T至少向右移动一个字符的位置)。
图1 Sunday算法不匹配的情况
分如下两种情况:
(1) S[L+i+1]在模式串T中没有出现。这个时候模式串T[0]移动到S[T+i+1]之后的字符的位置。如图2。
图2 Sunday算法移动的第1种情况
(2)S[L+i+1]在模式串中出现。这里S[L+i+1]从模式串T的右侧,即按T[M-1]、T[M-2]、…T[0]的次序查找。如果发现S[L+i+1]和T中的某个字符相同,则记下这个位置,记为k,1≤k≤M,且T[k]=S[L+i+1]。此时,应该把模式串T向右移动M-k个字符的位置,即移动到T[k]和S[L+i+1]对齐的位置。如图3。
图3 Sunday算法移动的第2种情况
依次类推,如果完全匹配了,则匹配成功;否则,再进行下一轮的移动,直到主串S的最右端结束。该算法最坏情况下的时间复杂度为O(N*M)。对于短模式串的匹配问题,该算法执行速度较快。
Sunday算法JAVA代码:
- package math;
- import java.util.ArrayList;
- import com.sun.org.apache.bcel.internal.generic.IFNONNULL;
- public class Sunday
- {
- public Sunday(){
- //
- }
- public ArrayList QfindChr(String str, String Sfind){
- int str_length = 0;
- int fin_length = 0;
- int find_count = 0;
- int start = 0;
- int moveNum = 0;
- ArrayList<Integer> index = new ArrayList<Integer>();
- if(Sfind.length() == 0 || str.length() == 0){
- return null;
- }
- if (str.length() < Sfind.length()){
- return null;
- }
- str_length = str.length();
- fin_length = Sfind.length();
- while (start + fin_length <= str_length)
- {
- moveNum++;
- boolean isfind = false;// 是否在这次移动中找到
- String s_temp = str.substring(start, start + fin_length);
- if (Sfind.equals(s_temp)) {
- index.add(start);//记录匹配字符的位置的第一个字符坐标
- find_count++;
- start = start + fin_length;
- isfind = true;
- }
- if (isfind == false)// 如果没找到计算下次移动位置
- {
- int forwardPos = QfindPos(str, Sfind, start, fin_length);
- start = forwardPos;
- }
- }
- System.out.println("move_count = " + moveNum);//移动次数显著减少
- return index;
- }
- //找字符在字符串(不算最后一个字符)的位置(倒数)
- //没找到返回fin_length,找到返回位置
- /// 找字符在字符串(不算最后一个字符)的位置(倒数);没找到返回str.length,找到返回位置
- public int QfindPos(String str, String find, int pos, int fin_length){
- int returnPos = str.length();
- char[] Schr = str.toCharArray();
- char[] Sfin = find.toCharArray();
- if ((pos + fin_length) < str.length())
- {
- char chrFind = Schr[pos + fin_length];//要找的字符
- if (fin_length >= 1)
- {
- if (find.lastIndexOf(chrFind) > -1)//如果find里存在chrFind字符
- {
- returnPos = pos + fin_length - find.lastIndexOf(chrFind);
- }
- else{//如果find里不存在chrFind字符
- returnPos = pos + fin_length + 1;
- }
- }
- }
- return returnPos;
- }
- public static void main(String[] args) {
- String str = "return1111chrFind1Pos = chrFind1 pos + fin_length -returnPos = pos + fin_length -returnPos"
- + " = pos + fin_length -returnPos = pos + fin_length -returnPos = pos + fin_length -returnPos = pos "
- + "+ fin_length -returnPos = pos + fin_length -returnPos = pos + fin_length -returnPos = pos + fin_length"
- + " -returnPos = pos + fin_length -returnPos = pos + fin_length - find.lastIndexOf(chrFind);returnPos = pos "
- + "+ fin_length -returnPos = pos + fin_length -returnPos = pos + fin_length -returnPos = pos + fin_length "
- + "-returnPos = pos + fin_length -returnPos = pos + fin_length -returnPos = pos + fin_length -returnPos = pos"
- + " + fin_length -returnPos = pos + fin_length -returnPos = pos + fin_length -returnPos = pos + fin_length -"
- + " find.lastIndexOf(chrFind);returnPos = pos + fin_length -returnPos = pos + fin_length -returnPos = pos +"
- + " fin_length -returnPos = pos + fin_length -returnPos = pos + fin_length -returnPos = pos + fin_length "
- + "-returnPos = pos + fin_length -returnPos = pos + fin_length -returnPos = pos + fin_length -returnPos = pos "
- + "+ fin_length -returnPos = pos + fin_length - find.lastIndexOf(chrFind1)";
- String find = "chrFind1";
- Sunday sunday = new Sunday();
- ArrayList<Integer> index = sunday.QfindChr(str, find);
- if(index == null){
- System.out.println("条件不符合");
- return;
- }
- System.out.println("count = "+index.size());
- for (int i = 0; i < index.size(); i++) {
- System.out.print("index:"+index.get(i)+", ");
- }
- }
- }