background image

DSaA 2012/2013 

String matching 

Data Structures and Algorithms  

background image

DSaA 2012/2013 

String matching 

String matching algorithms: 
• Naive 
• Rabin-Karp 
• Finite automaton 
• Knuth-Morris-Pratt (KMP) 

ε – epsilon, δ – delta, φ – phi, σ – sigma, π - pi 

background image

DSaA 2012/2013 

String matching problem 

We assume that the text is an array [1..n] of length and that the pattern 
is an array P[1..m] of length 

≤ n. We further assume that the elements of 

and are characters drawn from a finite alphabet 

S

. For example, we 

may have 

S

 = {0, 1} or 

S

 = {a, b,. . . , z}. The character arrays and are 

often called strings of characters. 

We say that pattern occurs with shift s in text (or, equivalently, that 
pattern occurs beginning at position s + 1 in text T) if 0 

≤ ≤ and 

[+ 1..m] = P[1..m] (that is, if [j] = Pj], for 1 

≤ ≤ m). If occurs 

with shift in , then we call valid shift; otherwise, we call an invalid 
shift
. The string-matching problem is the problem of finding all valid shifts 
with which a given pattern occurs in a given text 

Text T 

Pattern P 

b  a  b  a 

a  c  b  a 

a  a  b  a 

a  b  a 

b  b  a 

a  b  a 

a  b  a 

a  b  a 

3 occurrences  
of pattern with shifts 
3, 5, 9 

15 

9  10  11  12  13  14 

background image

DSaA 2012/2013 

Complexity - comparison 

Algorithm 

Preprocessing time 

Worse case 

Matching time 

Naive 

O((n-m+1)m) 

Rabin-Karp 

(m) 

O((n-m+1)m) 

Finite automaton 

 

O(m|

S

|) 

(n) 

Knutt-Morris-Pratt 

(m) 

(n) 

background image

DSaA 2012/2013 

Notation and terminology 

• We shall let 

S

* (read "sigma-star") denote the set of all 

finite-length strings formed using characters from the 

alphabet 

S

. In this lecture, we consider only finite-length 

strings. The zero-length empty string, denoted 

e

, also 

belongs to 

S

*. The length of a string is denoted |x|. The 

concatenation of two strings and y, denoted xy, has 

length |x| + |y| and consists of the characters from 

followed by the characters from y

• We say that a string is a prefix of a string x, denoted 

 x, if wy for some string y

S

*. Note that if 

⊏ x

then |w

≤ |x|. Similarly, we say that a string is a suffix 

of a string x, denoted 

⊐ x, if yw for some y

S

*. It 

follows from 

⊐ that |w| ≤ |x|. The empty string 

e

 is 

both a suffix and a prefix of every string. For example, 

we have ab 

⊏ abcca and cca ⊐ abcca. It is useful to note 

that for any strings and and any character a, we have 

x

if and only if xaya. Also note that ⊏ and ⊐ are 

transitive relations. 

background image

DSaA 2012/2013 

Notation and terminology 

• Overlapping-suffix lemma: 
  
Suppose that xy, and are strings such 

that x

and yz. If |x| ≤ |y|, then xy. If 

|x|

≥|y|, then yx. If |x| = |y|, then y

For brevity of notation, we shall denote the k-character prefix P[1..k]  
of the pattern P[1..m] by P

k

. Thus, P

0

 = 

e

 and P

m

 P[1..m]. Similarly,  

we denote the k-character prefix of the text as T

k

. Using this notation,  

we can state the string-matching problem as that of finding all shifts in  
the range 0 

≤ ≤ n-such that T

s+m

The test "y" is assumed to take time 

(+ 1), where is the length of the 

longest string such that z

and zy.  

background image

DSaA 2012/2013 

Naive algorithm - code 

 
{ 1} 
{ 2} 
{ 3} 
{ 4} 
{ 5} 

Naive_String_Matcher(T,P) 
n := length[T] 
m := length[P] 
for s:=0 to n-m do 
  if P[1..m]=T[s+1..s+m] then 
    print „patter occurs with shift” s 

T=a

n

,   P=a

m

 

(n-m+1)m character comparisons 

Worse-case complexity: O((n-m+1)m) 

T=„random”,   P=„random” 

average ≤2(n-m+1)  
  character comparisons 

Average complexity: O(n-m+1) 

T=a

n

,   P=a

m-1

background image

DSaA 2012/2013 

The Rabin-Karp algorithm - idea 

For expository purposes, let us assume that 

S

 = {0, 1, 2, . . . , 9}, so that each 

character is a decimal digit. (In the general case, we can assume that each character 

is a digit in radix-notation, where = |

S

|.) We can then view a string of 

consecutive characters as representing a length-decimal number. The character 

string 

„31415” thus corresponds to the decimal number 31,415. 

Given a pattern P[1..m], we let denote its corresponding decimal value. In a similar 

manner, given a text T[1..n], we let t

s

 denote the decimal value of the length-

substring T[+ 1..m], for = 0, 1, . . . , n-m. Certainly, t

s

 if and only if 

T[s+1..s+m] = P[1..m]; thus, is a valid shift if and only if t

s

 p. If we could compute 

in time 

(m) and all the t

s

 values in a total of 

(+ 1) time, then we could 

determine all valid shifts in time 

(m) + 

(n-m+1) = 

(n) by comparing with each 

of the t

s

's. We can compute in time 

(m) using Horner's rule: 

 

   

P[m] + 10 (P[- 1] + 10(P[

2] + · · · + 10(P[2] + 10P[1]) )). 

The value t

0

 can be similarly computed from T[1..m] in time 

(m). To compute the 

remaining values t

1

t

2

, . . . , t

n-m

 in time 

(m), it suffices to observe that t

s+1

 can be 

computed from t

s

 in constant time, since 

]

1

[

]

1

[

10

10

1

1

m

s

T

s

T

t

t

m

s

s

T=..314152..,   m=5,    t

s

=31415 

t

s+1

=10(31415-10000

*

3)+2=14152 

Problem! : and t

s

 may be too large to work with conveniently. 

background image

DSaA 2012/2013 

The Rabin-Karp algorithm 

• We can compute and the t

s

's modulo a 

suitable modulus q. 

2  3  5  9  0  2  3  1  4  1  5  2  6  7  3  9  9  2  1 

3  1  4  1  5 

8  9  3 11 0  1  7  8  4  5 10 11 7  9 

… 

… 

11 

q=13 

valid  
match 

spurious 
hit 

2  3  5  9  0  2 

8  9 

35902 ≡ (23590 – 3*10000)*10+2 (mod 13) 
  

    

≡ (8 – 2*3)*10 + 2 (mod 13) 

 

    

≡ 9 

 

q

m

s

T

h

s

T

t

d

t

s

s

mod

1

1

1

where 

q

d

h

m

mod

1

background image

10 

DSaA 2012/2013 

The Rabin-Karp algorithm - code 

 
{ 1} 
{ 2} 
{ 3} 
{ 4} 
{ 5} 
{ 6} 
{ 7} 
{ 8} 
{ 9} 
{10} 
{11} 
{12} 
{13} 
{14} 

Rabin_Karp_Matcher(T,P) 
n := length[T] 
m := length[P] 
h := d

m-1

 mod q 

p := 0 
t

0

 := 0 

for i:=1 to m do 
  p := (d*p+P[i]) mod q 
  t

o

 := (d*t

0

+T[i]) mod q 

for s:=0 to n-m do 
  if p=t

s

 then 

    if P[1..m]=T[s+1..s+m] then 
      print „patter occurs with shift” s 
  if s < n-m then 
    t

s+1

:=(d*(t

s

-T[s+1]*h)+T[s+m+1]) mod q 

The modulus is typically chosen as a prime such that dq just fits within  
one computer word (double word) 

background image

11 

DSaA 2012/2013 

The Rabin-Karp algorithm - analysis 

• RABIN-KARP-MATCHER takes 

(m) preprocessing time, and its 

matching time is 

((+ 1)m) in the worst case, since (like the naive 

string-matching algorithm) the Rabin-Karp algorithm explicitly verifies 
every valid shift. If = a

m

 and = a

n

, then the verifications take time 

((+ 1)m), since each of the + 1 possible shifts is valid. 

• In many applications, we expect few valid shifts (perhaps some 

constant of them); in these applications, the expected matching time 
of the algorithm is only O((+ 1) + cm) = O(n+m), plus the time 
required to process spurious hits. After a heuristic analysis on an 
assumption we can then expect that the number of spurious hits is 
O(n/q), since the chance that an arbitrary t

s

 will be equivalent to p

modulo q, can be estimated as 1/q. Since there are O(n) positions at 
which the test of line 10 fails and we spend O(m) time for each hit, the 
expected matching time taken by the Rabin-Karp algorithm is 

O(n) + O(m(n/q)), 

where is the number of valid shifts. This running time is O(n) if v=O(1) 

and we choose m. That is, if the expected number of valid shifts is 
small (O(1)) and the prime is chosen to be larger than the length of 
the pattern, then we can expect the Rabin-Karp procedure to use only 
O(m) matching time. Since 

 n, this expected matching time is 

O(n). 

background image

12 

DSaA 2012/2013 

Finite automaton 

• A finite automaton is a 5-tuple (Qq

0

A

S

d

), where: 

– is a finite set of states
– q

0

is the start state

– A

is a distinguished set of accepting states

S

 is a finite input alphabet

d

 is a function from 

× 

S

 into Q, called the transition function of M

The finite automaton begins in state q

0

 and reads the characters of its 

input string one at a time. If the automaton is in state and reads 

input character a, it moves ("makes a transition") from state to 

state 

d

(qa). Whenever its current state is a member of A, the 

machine is said to have accepted the string read so far. An input 

that is not accepted is said to be rejected. 

1  0 
0  0 


Q = {0, 1}, q

o

=0, A={1} 

S

 = { a, b},

 

background image

13 

DSaA 2012/2013 

Automaton 

– cont. 

• A finite automaton induces a function 

f

 , called the final-state 

function, from 

S

* to such that 

f

(w) is the state ends up in after 

scanning the string w. Thus, M accepts a string if and only if 

f

(w)

A. The function 

f

 is defined by the recursive relation: 

 
 

 

S

S

a

w

for

a

w

wa

q

,

,

,

*

0

f

d

f

e

f

We define an auxiliary function 

s

 , called the suffix function corresponding to P

The function 

s

 is a mapping from 

S

* to {0, 1, . . . , m} such that 

s

(x) is the length 

of the longest prefix of that is a suffix of x

s

(x) = max { k: P

⊐ x} 

We define the string-matching automaton that corresponds to a given pattern 
P[1..m] as follows: 

- The state set is {0, 1, . . . , m}. The start state q

0

 is state 0, and state is the 

only accepting state. 

- The transition function 

d

 is defined by the following equation, for any state 

and character a

 

 

a

P

a

q

q

s

d

,

background image

14 

DSaA 2012/2013 

Automaton - example 

1  0 
1  2 




3  0 

0  a 

1  4 

0  b 

5  0 
1  4 



7  0 

0  a 

1  2 

9  10 11 
a  b  a 
7  2  3 

6  7  8 
b  a  c 
4  5  6 

3  4  5 
a  b  a 
3  4  5 

-  1  2 
-  a  b 

0  1  2 

T[i] 
state 

f

(T[i]) 

b,c 

b,c 

b,c 

b,c 

background image

15 

DSaA 2012/2013 

Finite automaton matcher - code 

 
{ 1} 
{ 2} 
{ 3} 
{ 4} 
{ 5} 
{ 6} 
{ 7} 

Finite_Automaton_Matcher(T,P) 
n := length[T] 
q := 0 
for i:=1 to n do 
  q := 

d

(q,T[i])  

  if q = m then 
    s := i - m 
    print „patter occurs with shift” s 

 
{ 1} 
{ 2} 
{ 3} 
{ 4} 
{ 5} 
{ 6} 
{ 7} 
{ 8} 
{ 9} 

Compute_Transition_Function(P,

S

m := length[P] 
for q:=0 to m do 
  for
 a

S

 do 

    k := min(m+1,q+2) 
    repeat 
      k := k – 1 
    until P

k

 

⊐ P

q

    

d

(q,a) := k 

return 

background image

16 

DSaA 2012/2013 

Finite automaton matcher - analysis 

• The running time of COMPUTE-TRANSITION-

FUNCTION is O(m

3

|

S

|), because the outer loops 

contribute a factor of m|

S

|, the inner repeat loop 

can run at most + 1 times, and the test P

⊐ 

P

q

on line 6 can require comparing up to 

characters.  

• Much faster procedures exist; the time required to 

compute 

d

 from can be improved to O(m|

S

|) by 

utilizing some cleverly computed information 
about the pattern P. With this improved 
procedure for computing 

d

, we can find all 

occurrences of a length-pattern in a length-
text over an alphabet 

S

 with O(|

S

|) 

preprocessing time and 

(n) matching time. 

background image

17 

DSaA 2012/2013 

Knuth-Morris-

Pratt’s idea  

b  a  c  b  a  b  a  b  a  a  b  c  b  a  b 

a  b  a  b  a  c  a 

b  a  c  b  a  b  a  b  a  a  b  c  b  a  b 

a  b  a  b  a  c  a 

s’ 

a  b  a  b  a 

a  b  a 

P

q

 

P

k

 

• Given a pattern P[1..m], the 

prefix function for the pattern 
is the function 

p

 : {1, 2, . . . , 

m

{0, 1, . . . , - 1} such 

that 

p

[q]=max{k: k<q and P

k

 

⊐ P

q

• That is, 

p

[q] is the length of the 

longest prefix of that is a 
proper suffix of P

q

9  10 

c  a 

0  1 

6  7  8 
b  a  b 
4  5  6 

3  4  5 
a  b  a 
1  2  3 

1  2 
a  b 
0  0 

P[i] 

p

[i] 

background image

18 

DSaA 2012/2013 

KMP - code 

 
{ 1} 
{ 2} 
{ 3} 
{ 4} 
{ 5} 
{ 6} 
{ 7} 
{ 8} 
{ 9} 
{10} 
{11} 
{12} 

KMP_Matcher(T,P) 
n := length[T] 
m := length[P] 

p

 := Compute_Prefix_Function(P) 

q :=0 
for i:=1 to n do 
  while
 q>0 and P[q+1]≠T[i] do 
    q := 

p

[q] 

  if P[q+1] = T[i] then 
    q := q+1 
  if q=m then 
    print „patter occurs with shift” i-m 
    q := 

p

[q] 

 
{ 1} 
{ 2} 
{ 3} 
{ 4} 
{ 5} 
{ 6} 
{ 7} 
{ 8} 
{ 9} 
{10} 

Compute_Prefix_Function(P) 
m := length[P] 

p

[1] := 0 

k := 0 
for q:=2 to m do 
  while
 k>0 and P[k+1]

≠P[q] do 

    k := 

p

[k] 

  if P[k+1]=P[q] then 
    k := k+1 
  

p

[q] := k 

return 

9  10 

c  a 

0  1 

6  7  8 
b  a  b 
4  5  6 

3  4  5 
a  b  a 
1  2  3 

1  2 
a  b 
0  0 

P[i] 

p

[i] 

background image

19 

DSaA 2012/2013 

KMP - analysis 

• The running time of COMPUTE-PREFIX-FUNCTION is 

(m), using 

the potential method of amortized analysis. We associate a potential 
of with the current state of the algorithm. This potential has an 
initial value of 0, by line 3. Line 6 decreases whenever it is 
executed, since 

p

[k] < k. Since 

p

[k] = 0 for all k, however, can 

never become negative. The only other line that affects is line 8, 
which increases by at most one during each execution of the for 
loop body. Since upon entering the for loop, and since is 
incremented in each iteration of the for loop body, always 
holds. (This justifies the claim that 

p

[q] < as well, by line 9.) We 

can pay for each execution of the while loop body on line 6 with the 
corresponding decrease in the potential function, since 

p

[k] < k

Line 8 increases the potential function by at most one, so that the 
amortized cost of the loop body on lines 5-9 is O(1). Since the 
number of outer-loop iterations is 

(m), and since the final potential 

function is at least as great as the initial potential function, the total 
actual worst-case running time of COMPUTE-PREFIX-FUNCTION 
is 

(m). 

• A similar amortized analysis, using the value of as the potential 

function, shows that the matching time of KMP-MATCHER is 

(n).