.

字串匹配——Sunday演算法
基本思想及舉例
Sunday演算法由Daniel M.Sunday在1990年提出,它的思想跟BM演算法很相似:1

只不過Sunday演算法是從前往後匹配,在匹配失敗時關注的是主串中參加匹配的最末位字元的下一位字元。

如果該字元沒有在模式串中出現則直接跳過,即移動位數 = 模式串長度 + 1;
否則,其移動位數 = 模式串長度 - 該字元最右出現的位置(以0開始) = 模式串中該字元最右出現的位置到尾部的距離 + 1。
下面舉個例子說明下Sunday演算法。假定現在要在主串”substring searching”中查詢模式串”search”。

剛開始時,把模式串與文主串左邊對齊: 


結果發現在第2個字元處發現不匹配,不匹配時關注主串中參加匹配的最末位字元的下一位字元,即標粗的字元 i,因為模式串search中並不存在i,所以模式串直接跳過一大片,向右移動位數 = 匹配串長度 + 1 = 6 + 1 = 7,從 i 之後的那個字元(即字元n)開始下一步的匹配,如下圖: 


結果第一個字元就不匹配,再看主串中參加匹配的最末位字元的下一位字元,是’r’,它出現在模式串中的倒數第3位,於是把模式串向右移動3位(m - 3 = 6 - 3 = r 到模式串末尾的距離 + 1 = 2 + 1 =3),使兩個’r’對齊,如下: 


匹配成功。

回顧整個過程,我們只移動了兩次模式串就找到了匹配位置,緣於Sunday演算法每一步的移動量都比較大,效率很高。

偏移表
在預處理中,計算大小為|∑||∑|的偏移表。

shift[w]={m−max{i<m|P[i]=w}m+1 if w is in P[0..m−1] 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(|∑||∑|)


--------------------- 
作者:Switch_vov 
來源:CSDN 
原文:https://blog.csdn.net/q547550831/article/details/51860017 
版權宣告:本文為博主原創文章,轉載請