Passed
Push — master ( 39de41...cc7e60 )
by Xianshun
01:46
created

KnuthMorrisPratt.__init__()   A

Complexity

Conditions 4

Size

Total Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
c 1
b 0
f 0
dl 0
loc 14
rs 9.2
1
from abc import ABCMeta, abstractmethod
2
3
4
class SubstringSearch(object):
5
    __metaclass__ = ABCMeta
6
7
    @abstractmethod
8
    def search_in(self, text):
9
        pass
10
11
12
class BruteForceSubstringSearch(SubstringSearch):
13
    def __init__(self, pattern):
14
        self.pattern = pattern
15
16
    def search_in(self, text):
17
        n = len(text)
18
19
        for i in range(n - len(self.pattern)):
20
            J = i
21
            for j in range(len(self.pattern)):
22
                k = i + j
23
                if text[k] != self.pattern[j]:
24
                    J = -1
25
                    break
26
            if J != -1:
27
                return J
28
29
        return -1
30
31
32
def char_at(text, d):
33
    return ord(text[d])
34
35
36
class RabinKarp(SubstringSearch):
37
    patHash = None
38
    Q = 1573773197
39
    R = 256
40
    M = None
41
    RM = None
42
43
    def __init__(self, pat):
44
        h = 1
45
        self.M = len(pat)
46
        for i in range(1, self.M):
47
            h = (h * RabinKarp.R) % RabinKarp.Q
48
49
        self.RM = h
50
51
        self.patHash = self.hash(pat, self.M)
52
53
    def hash(self, text, M):
54
        h = 0
55
        for d in range(M):
56
            h = (h * RabinKarp.R + char_at(text, d)) % RabinKarp.Q
57
        return h
58
59
    def search_in(self, text):
60
        text_hash = self.hash(text, self.M)
61
        if text_hash == self.patHash:
62
            return 0
63
        for i in range(self.M, len(text)):
64
            text_hash = (text_hash + RabinKarp.Q - self.RM * char_at(text, i - self.M) % RabinKarp.Q) % RabinKarp.Q
65
            text_hash = (text_hash * RabinKarp.R + char_at(text, i)) % RabinKarp.Q
66
            if text_hash == self.patHash:
67
                return i - self.M + 1
68
        return -1
69
70
71
class BoyerMoore(SubstringSearch):
72
    right = None
73
    R = 256
74
    pattern = None
75
76
    def __init__(self, pattern):
77
        self.pattern = pattern
78
        self.right = [0] * BoyerMoore.R
79
        for i in range(len(pattern)):
80
            self.right[char_at(pattern, i)] = i
81
82
    def search_in(self, text):
83
        n = len(text)
84
        m = len(self.pattern)
85
86
        skip = 1
87
        i = 0
88
        while i <= n - m:
89
            for j in range(m-1, -1, -1):
90
                if char_at(text, i+j) != char_at(self.pattern, j):
91
                    skip = max(1, j - self.right[char_at(text, i+j)])
92
                    break
93
                if j == 0:
94
                    return i
95
            i += skip
96
97
        return -1
98
99
100
class KnuthMorrisPratt(SubstringSearch):
101
102
    dfs = None
103
    M = None
104
    R = 256
105
106
    def __init__(self, pattern):
107
        self.M = len(pattern)
108
109
        self.dfs = [None] * self.R
110
        for i in range(self.R):
111
            self.dfs[i] = [0] * self.M
112
        self.dfs[0][0] = 1
113
114
        X = 0
115
        for j in range(self.M):
116
            for i in range(self.R):
117
                self.dfs[i][j] = self.dfs[i][X]
118
            self.dfs[char_at(pattern, j)][j] = j+1
119
            X = self.dfs[char_at(pattern, j)][X]
120
121
    def search_in(self, text):
122
123
        n = len(text)
124
125
        j = 0
126
        for i in range(n):
127
            j = self.dfs[char_at(text, i)][j]
128
            if j == self.M:
129
                return i - self.M
130
131
        return -1