字符串匹配——Sunday算法

原文地址: http://blog.csdn.net/q547550831/article/details/51860017


字符串匹配——Sunday算法

基本思想及举例

Sunday算法由Daniel M.Sunday在1990年提出,它的思想跟BM算法很相似:1

只不过Sunday算法是从前往后匹配,在匹配失败时关注的是主串中参加匹配的最末位字符的下一位字符。

  • 如果该字符没有在模式串中出现则直接跳过,即移动位数 = 模式串长度 + 1;
  • 否则,其移动位数 = 模式串长度 - 该字符最右出现的位置(以0开始) = 模式串中该字符最右出现的位置到尾部的距离 + 1。

下面举个例子说明下Sunday算法。假定现在要在主串”substring searching”中查找模式串”search”。

  • 刚开始时,把模式串与文主串左边对齐: 
    Sunday1

  • 结果发现在第2个字符处发现不匹配,不匹配时关注主串中参加匹配的最末位字符的下一位字符,即标粗的字符 i,因为模式串search中并不存在i,所以模式串直接跳过一大片,向右移动位数 = 匹配串长度 + 1 = 6 + 1 = 7,从 i 之后的那个字符(即字符n)开始下一步的匹配,如下图: 
    Sunday2

  • 结果第一个字符就不匹配,再看主串中参加匹配的最末位字符的下一位字符,是’r’,它出现在模式串中的倒数第3位,于是把模式串向右移动3位(m - 3 = 6 - 3 = r 到模式串末尾的距离 + 1 = 2 + 1 =3),使两个’r’对齐,如下: 
    Sunday3

  • 匹配成功。

    回顾整个过程,我们只移动了两次模式串就找到了匹配位置,缘于Sunday算法每一步的移动量都比较大,效率很高。

偏移表

在预处理中,计算大小为|||∑|的偏移表。

shift[w]={mmax{i<m|P[i]=w}m+1 if w is in P[0..m1] otherwise shift[w]={m−max{i<m|P[i]=w} if w is in P[0..m−1]m+1 otherwise 

例如: P = “search” 
m = 6 
shift[s] = 6 - max(s的位置) = 6 - 0 = 6 
shift[e] = 6 - max(e的位置) = 6 - 1 = 5 
shift[a] = 6 - max(a的位置) = 6 - 2 = 4 
shift[r] = 6 - max(r的位置) = 6 - 3 = 3 
shift[c] = 6 - max(c的位置) = 6 - 4 = 2 
shift[h] = 6 - max(h的位置) = 6 - 5 = 1 
shift[其他] = m + 1 = 6 + 1 = 7

伪代码

Sunday(T, P)
01 n <- the length of T
02 m <- the length of P
03 // computer the shift table for P
04 for c <- 0 to the length of OffsetTable - 1
05  shift[c] = m + 1
06 for k <- 0 to m - 1
07  shift[P[k]] = m - k
08 // search
09 s <- 0
10 while s ≤ n - m do
11  j <- 0  // start from the begin
12  // check if T[s..s+m-1] = P[0..m-1]
13  while T[s + j] = P[j] do
14      j <- j + 1
15      if j ≥ m then return s
16  s <- s + shift[T[s + m]]
17 return -1
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

算法实现

const int maxNum = 1005;
int shift[maxNum];
int Sunday(const string& T, const string& P) {
    int n = T.length();
    int m = P.length();

    // 默认值,移动m+1位
    for(int i = 0; i < maxNum; i++) {
        shift[i] = m + 1;
    }

    // 模式串P中每个字母出现的最后的下标
    // 所对应的主串参与匹配的最末位字符的下一位字符移动到该位,所需要的移动位数
    for(int i = 0; i < m; i++) {
        shift[P[i]] = m - i;
    }

    // 模式串开始位置在主串的哪里
    int s = 0;
    // 模式串已经匹配到的位置
    int j;
    while(s <= n - m) {
        j = 0;
        while(T[s + j] == P[j]) {
            j++;
            // 匹配成功
            if(j >= m) {
                return s;
            }
        }
        // 找到主串中当前跟模式串匹配的最末字符的下一个字符
        // 在模式串中出现最后的位置
        // 所需要从(模式串末尾+1)移动到该位置的步数
        s += shift[T[s + m]];
    }
    return -1;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

测试主程序

#include <iostream>
#include <string>

using namespace std;

const int maxNum = 1005;

int shift[maxNum];
int Sunday(const string& T, const string& P) {
    int n = T.length();
    int m = P.length();

    // 默认值,移动m+1位
    for(int i = 0; i < maxNum; i++) {
        shift[i] = m + 1;
    }

    // 模式串P中每个字母出现的最后的下标
    // 所对应的主串参与匹配的最末位字符的下一位字符移动到该位,所需要的移动位数
    for(int i = 0; i < m; i++) {
        shift[P[i]] = m - i;
    }

    // 模式串开始位置在主串的哪里
    int s = 0;
    // 模式串已经匹配到的位置
    int j;
    while(s <= n - m) {
        j = 0;
        while(T[s + j] == P[j]) {
            j++;
            // 匹配成功
            if(j >= m) {
                return s;
            }
        }
        // 找到主串中当前跟模式串匹配的最末字符的下一个字符
        // 在模式串中出现最后的位置
        // 所需要从(模式串末尾+1)移动到该位置的步数
        s += shift[T[s + m]];
    }
    return -1;
}



/**
IN
at the thought of
though

OUT
7
**/
int main() {
    // 主串和模式串
    string T, P;
    while(true) {
        // 获取一行
        getline(cin, T);
        getline(cin, P);
        int res = Sunday(T, P);
        if(res == -1) {
            cout << "主串和模式串不匹配。" << endl;
        } else {
            cout << "模式串在主串的位置为:" << res << endl;
        }
    }
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70

输出数据

at the thought of
though
模式串在主串的位置为:7
abcd
d
模式串在主串的位置为:3
asd
d
模式串在主串的位置为:2
adasfasfasfasgaegagfasdf
gf
模式串在主串的位置为:18
gagewgwe
wefgwef
主串和模式串不匹配。
gwagweg
g
模式串在主串的位置为:0
gergregeagbb
bb
模式串在主串的位置为:10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

算法分析2

  • Sunday预处理阶段的时间为:O(|||∑| + m)

  • 最坏情况下时间复杂度为:O(nm)

  • 平均时间复杂度:O(n)

  • 空间复杂度:O(|||∑|)


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值