Fast Pattern Matching in Strings

About this paperTiếng Việt

This paper is under a licence that does not allow it to be reproduced, so what is here is its front matter and a short abstract. Read it at the publisher's page.

English
日本語

Abstract. An algorithm is presented which finds all occurrences of one given string within another, in running time proportional to the sum of the lengths of the strings. The constant of proportionality is low enough to make this algorithm of practical use, and the procedure can also be extended to deal with some more general pattern-matching problems. A theoretical application of the algorithm shows that the set of concatenations of even palindromes, i.e., the language {ααR}∗\{\alpha\alpha^R\}^*, can be recognized in linear time. Other algorithms which run even faster on the average are also considered.

Key words. pattern, string, text-editing, pattern-matching, trie memory, searching, period of a string, palindrome, optimum algorithm, Fibonacci string, regular expression

Text-editing programs are often required to search through a string of characters looking for instances of a given "pattern" string; we wish to find all positions, or perhaps only the leftmost position, in which the pattern occurs as a contiguous substring of the text. For example, catenary contains the pattern ten, but we do not regard canary as a substring.

The obvious way to search for a matching pattern is to try searching at every starting position of the text, abandoning the search as soon as an incorrect character is found. But this approach can be very inefficient, for example when we are looking for an occurrence of aaaaaaaabaaaaaaaab in aaaaaaaaaaaaaaabaaaaaaaaaaaaaaab. When the pattern is anba^nb and the text is a2nba^{2n}b, we will find ourselves making (n+1)2(n+1)^2 comparisons of characters. Furthermore, the traditional approach involves "backing up" the input text as we go through it, and this can add annoying complications when we consider the buffering operations that are frequently involved.

In this paper we describe a pattern-matching algorithm which finds all occurrences of a pattern of length mm within a text of length nn in O(m+n)O(m+n) units of time, without "backing up" the input text. The algorithm needs only O(m)O(m) locations of internal memory if the text is read from an external file, and only O(log⁡m)O(\log m) units of time elapse between consecutive single-character inputs. All of the constants of proportionality implied by these "O" formulas are independent of the alphabet size.

* Received by the editors August 29, 1974, and in revised form April 7, 1976. † Computer Science Department, Stanford University, Stanford, California 94305. The work of this author was supported in part by the National Science Foundation under Grant GJ 36473X and by the Office of Naval Research under Contract NR 044-402. ‡ Xerox Palo Alto Research Center, Palo Alto, California 94304. The work of this author was supported in part by the National Science Foundation under Grant GP 7635 at the University of California, Berkeley. ¶ Artificial Intelligence Laboratory, Massachusetts Institute of Technology, Cambridge, Massachusetts 02139. The work of this author was supported in part by the National Science Foundation under Grant GP-6945 at University of California, Berkeley, and under Grant GJ-992 at Stanford University.

We shall first consider the algorithm in a conceptually simple but somewhat inefficient form. Sections 3 and 4 of this paper discuss some ways to improve the efficiency and to adapt the algorithm to other problems. Section 5 develops the underlying theory, and § 6 uses the algorithm to disprove the conjecture that a certain context-free language cannot be recognized in linear time. Section 7 discusses the origin of the algorithm and its relation to other recent work. Finally, § 8 discusses still more recent work on pattern matching.

  1. Informal development. The idea behind this approach to pattern matching is perhaps easiest to grasp if we imagine placing the pattern over the text and sliding it to the right in a certain way. Consider for example a search for the pattern abcabcacababcabcacab in the text babcbabcaabcabcabcacabcbabcbabcaabcabcabcacabc; initially we place the pattern at the extreme left and prepare to scan the leftmost character of the input text:
abcabcacaba b c a b c a c a b
babcbabcabcaabcabcabcacabcb a b c b a b c a b c a a b c a b c a b c a c a b c

↑

The arrow here indicates the current text character; since it points to bb, which doesn’t match that aa, we shift the pattern one space right and move to the next input character:

abcabcacaba b c a b c a c a b
babcbabcabcaabcabcabcacabcb a b c b a b c a b c a a b c a b c a b c a c a b c

↑

Now we have a match, so the pattern stays put while the next several characters are scanned. Soon we come to another mismatch:

abcabcacaba b c a b c a c a b
babcbabcaabcabcabcacabcb a b c b a b c a a b c a b c a b c a c a b c

↑

At this point we have matched the first three pattern characters but not the fourth, so we know that the last four characters of the input have been abcxa b c x where x≠ax \neq a; we don’t have to remember the previously scanned characters, since our position in the pattern yields enough information to recreate them. In this case, no matter what xx is (as long as it’s not aa), we deduce that the pattern can immediately be shifted four more places to the right; one, two, or three shifts couldn’t possibly lead to a match.

Soon we get to another partial match, this time with a failure on the eighth pattern character:

abcabcacaba b c a b c a c a b
babcbabcabcaabcabcabcacabcb a b c b a b c a b c a a b c a b c a b c a c a b c

Now we know that the last eight characters were abcabcaxa b c a b c a x, where x≠cx \neq c. The pattern should therefore be shifted three places to the right:

abcabcacabbabcbabcabcaabcabcacabca b c a b c a c a b \\ b a b c b a b c a b c a a b c a b c a c a b c

We try to match the new pattern character, but this fails too, so we shift the pattern four (not three or five) more places. That produces a match, and we continue scanning until reaching another mismatch on the eighth pattern character:

abcabcacabbabcbabcabcaabcabcacabca b c a b c a c a b \\ b a b c b a b c a b c a a b c a b c a c a b c

Again we shift the pattern three places to the right; this time a match is produced, and we eventually discover the full pattern:

abcabcacabbabcbabcabcaabcabcacabca b c a b c a c a b \\ b a b c b a b c a b c a a b c a b c a c a b c

The play-by-play description for this example indicates that the pattern-matching process will run efficiently if we have an auxiliary table that tells us exactly how far to slide the pattern, when we detect a mismatch at its jjth character pattern[j]pattern[j]. Let next[j]next[j] be the character position in the pattern which should be checked next after such a mismatch, so that we are sliding the pattern j−next[j]j - next[j] places relative to the text. The following table lists the appropriate values:

j=1 2 3 4 5 6 7 8 9 10j = 1\ 2\ 3\ 4\ 5\ 6\ 7\ 8\ 9\ 10
pattern[j]=a b c a b c a c a bpattern[j] = a\ b\ c\ a\ b\ c\ a\ c\ a\ b
next[j]=0 1 1 0 1 1 0 5 0 1next[j] = 0\ 1\ 1\ 0\ 1\ 1\ 0\ 5\ 0\ 1

(Note that next[j]=0next[j] = 0 means that we are to slide the pattern all the way past the current text character.) We shall discuss how to precompute this table later; fortunately, the calculations are quite simple, and we will see that they require only O(m)O(m) steps.

At each step of the scanning process, we move either the text pointer or the pattern, and each of these can move at most nn times; so at most 2n2n steps need to be performed, after the nextnext table has been set up. Of course the pattern itself doesn’t really move; we can do the necessary operations simply by maintaining the pointer variable jj.

  1. Programming the algorithm. The pattern-match process has the general form
            
              place pattern at left;
while pattern not fully matched
    and text not exhausted do
        begin
            while pattern character differs from current text character
                do shift pattern appropriately;
            advance to next character of text;
        end;
            
          

For convenience, let us assume that the input text is present in an array text[1:n]text[1:n], and that the pattern appears in pattern[1:m]pattern[1:m]. We shall also assume that m>0m > 0, i.e., that the pattern is nonempty. Let kk and jj be integer variables such that text[k]text[k] denotes the current text character and pattern[j]pattern[j] denotes the corresponding pattern character; thus, the pattern is essentially aligned with positions p+1p + 1 through p+mp + m of the text, where k=p+jk = p + j. Then the above program takes the following simple form:

j:=k:=1;j := k := 1;
            
              while j ≤ m and k ≤ n do
    begin
        while j > 0 and text[k] ≠ pattern[j]
            do j := next[j];
        k := k + 1; j := j + 1;
    end;
            
          

If j>mj > m at the conclusion of the program, the leftmost match has been found in positions k−mk - m through k−1k - 1; but if j≤mj \leq m, the text has been exhausted. (The and operation here is the “conditional and” which does not evaluate the relation text[k]≠pattern[j]text[k] \neq pattern[j] unless j>0j > 0.) The program has a curious feature, namely that the inner loop operation “j:=next[j]j := next[j]” is performed no more often than the outer loop operation “k:=k+1k := k + 1”; in fact, the inner loop is usually performed somewhat less often, since the pattern generally moves right less frequently than the text pointer does.

To prove rigorously that the above program is correct, we may use the following invariant relation: “Let p=k−jp = k - j (i.e., the position in the text just preceding the first character of the pattern, in our assumed alignment). Then we have text[p+i]=pattern[i]text[p + i] = pattern[i] for 1≤i<j1 \leq i < j (i.e., we have matched the first j−1j - 1 characters of the pattern, if j>0j > 0); but for 0≤t<p0 \leq t < p we have text[t+i]≠pattern[i]text[t + i] \neq pattern[i] for some ii, where 1≤i≤m1 \leq i \leq m (i.e., there is no possible match of the entire pattern to the left of pp).”

The program will of course be correct only if we can compute the nextnext table so that the above relation remains invariant when we perform the operation j:=next[j]j := next[j]. Let us look at that computation now. When the program sets j := next[j], we know that j > 0, and that the last j characters of the input up to and including text[k] were

pattern[1]…pattern[j−1]xpattern[1] \ldots pattern[j-1] x

where x≠pattern[j]x \neq pattern[j]. What we want is to find the least amount of shift for which these characters can possibly match the shifted pattern; in other words, we want next[j] to be the largest i less than j such that the last i characters of the input were

pattern[1]…pattern[i−1]xpattern[1] \ldots pattern[i-1] x

and pattern[i]≠pattern[j]pattern[i] \neq pattern[j]. (If no such i exists, we let next[j] = 0.) With this definition of next[j] it is easy to verify that text[t+1] ... text[k] \neq pattern[1] \ldots pattern[k-1] for k - j \leq t < k - next[j]; hence the stated relation is indeed invariant, and our program is correct.

Now we must face up to the problem we have been postponing, the task of calculating next[j] in the first place. This problem would be easier if we didn't require pattern[i]≠pattern[j]pattern[i] \neq pattern[j] in the definition of next[j], so we shall consider the easier problem first. Let f[j] be the largest i less than j such that pattern[1]…pattern[i−1]=pattern[j−i+1]…pattern[j−1]pattern[1] \ldots pattern[i-1] = pattern[j-i+1] \ldots pattern[j-1]; since this condition holds vacuously for i=1i = 1, we always have f[j]≥1f[j] \geq 1 when j>1j > 1. By convention we let f[1]=0f[1] = 0.

The pattern used in the example of § 1 has the following f table:

j=12345678910pattern[j]=abcabcacabf[j]=0111234512.\begin{array}{ccccccccccc} j = 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 & 10 \\ pattern[j] = a & b & c & a & b & c & a & c & a & b \\ f[j] = 0 & 1 & 1 & 1 & 2 & 3 & 4 & 5 & 1 & 2. \end{array}

If pattern[j]=pattern[f[j]]pattern[j] = pattern[f[j]] then f[j+1]=f[j]+1f[j+1] = f[j]+1; but if not, we can use essentially the same pattern-matching algorithm as above to compute f[j+1]f[j+1], with text=pattern!text = pattern! (Note the similarity of the f[j]f[j] problem to the invariant condition of the matching algorithm. Our program calculates the largest j less than or equal to k such that pattern[1]…pattern[j−1]=text[k−j+1]…text[k−1]pattern[1] \ldots pattern[j-1] = text[k-j+1] \ldots text[k-1], so we can transfer the previous technology to the present problem.) The following program will compute f[j+1]f[j+1], assuming that f[j]f[j] and next[1]next[1], ..., next[j−1]next[j-1] have already been calculated:

t:=f[j];t := f[j];
while t>0 and pattern[j]≠pattern[t]\textbf{while } t > 0 \textbf{ and } pattern[j] \neq pattern[t]
do t:=next[t];\textbf{do } t := next[t];
f[j+1]:=t+1;f[j+1] := t + 1;

The correctness of this program is demonstrated as before; we can imagine two copies of the pattern, one sliding to the right with respect to the other. For example, suppose we have established that f[8]=5f[8] = 5 in the above case; let us consider the computation of f[9]f[9]. The appropriate picture is

abcabcacababcabcacab↑\begin{array}{cccccccccc} a & b & c & a & b & c & a & c & a & b \\ a & b & c & a & b & c & a & c & a & b \\ \uparrow \end{array}

Since pattern[8]≠bpattern[8] \neq b, we shift the upper copy right, knowing that the most recently scanned characters of the lower copy were a b c a xa\ b\ c\ a\ x for x≠bx \neq b. The nextnext table tells us to shift right four places, obtaining

abcabcacababcabcacab↑\begin{array}{cccccccccc} a & b & c & a & b & c & a & c & a & b \\ a & b & c & a & b & c & a & c & a & b \\ \uparrow \end{array}

and again there is no match. The next shift makes t=0t = 0, so f[9]=1f[9] = 1.

Once we understand how to compute ff, it is only a short step to the computation of next[j]next[j]. A comparison of the definitions shows that, for j>1j > 1,

next[j]={f[j],if pattern[j]≠pattern[f[j]];next[f[j]],if pattern[j]=pattern[f[j]].next[j] = \begin{cases} f[j], & \text{if } pattern[j] \neq pattern[f[j]]; \\ next[f[j]], & \text{if } pattern[j] = pattern[f[j]]. \end{cases}

Therefore we can compute the nextnext table as follows, without ever storing the values of f[j]f[j] in memory.

j:=1;t:=0;next[1]:=0;j := 1; t := 0; next[1] := 0;
            
              while j < m do
    begin comment t = f[j];
        while t > 0 and pattern[j] ≠ pattern[t]
            do t := next[t];
        t := t + 1; j := j + 1;
        if pattern[j] = pattern[t]
            then next[j] := next[t]
            else next[j] := t;
    end.
            
          

This program takes O(m)O(m) units of time, for the same reason as the matching program takes O(n)O(n): the operation t:=next[t]t := next[t] in the innermost loop always shifts the upper copy of the pattern to the right, so it is performed a total of mm times at most. (A slightly different way to prove that the running time is bounded by a constant times mm is to observe that the variable tt starts at 0 and it is increased, m−1m - 1 times, by 1; furthermore its value remains nonnegative. Therefore the operation t:=next[t]t := next[t], which always decreases tt, can be performed at most m−1m - 1 times.)

To summarize what we have said so far: Strings of text can be scanned efficiently by making use of two ideas. We can precompute “shifts”, specifying how to move the given pattern when a mismatch occurs at its jjth character; and this precomputation of “shifts” can be performed efficiently by using the same principle, shifting the pattern against itself.

  1. Gaining efficiency. We have presented the pattern-matching algorithm in a form that is rather easily proved correct; but as so often happens, this form is not very efficient. In fact, the algorithm as presented above would probably not be competitive with the naive algorithm on realistic data, even though the naive algorithm has a worst-case time of order mm times nn instead of mm plus nn, because the chance of this worst case is rather slim. On the other hand, a well-implemented form of the new algorithm should go noticeably faster because there is no backing up after a partial match.

It is not difficult to see the source of inefficiency in the new algorithm as presented above: When the alphabet of characters is large, we will rarely have a partial match, and the program will waste a lot of time discovering rather awkwardly that text[k]≠pattern[1]text[k] \neq pattern[1] for k=1,2,3,…k = 1, 2, 3, \ldots. When j=1j = 1 and text[k]≠pattern[1]text[k] \neq pattern[1], the algorithm sets j:=next[1]=0j := next[1] = 0, then discovers that j=0j = 0, then increases kk by 1, then sets jj to 1 again, then tests whether or not 1 is ≤m\leq m, and later it tests whether or not 1 is greater than 0. Clearly we would be much better off making j=1j = 1 into a special case.

The algorithm also spends unnecessary time testing whether j>mj > m or k>nk > n. A fully-matched pattern can be accounted for by setting pattern[m+1]="@"pattern[m + 1] = "@" for some impossible character @ that will never be matched, and by letting next[m+1]=−1next[m + 1] = -1; then a test for j<0j < 0 can be inserted into a less-frequently executed part of the code. Similarly we can for example set text[n+1]="⊥"text[n + 1] = "\bot" (another impossible character) and text[n+2]=pattern[1]text[n + 2] = pattern[1], so that the test for k>nk > n needn’t be made very often. (See [17] for a discussion of such more or less mechanical transformations on programs.)

The following form of the algorithm incorporates these refinements.

            
              a := pattern[1];
pattern[m + 1] := '@'; next[m + 1] := -1;
text[n + 1] := '\bot'; text[n + 2] := a;
j := k := 1;
get started: comment j = 1;
    while text[k] ≠ a do k := k + 1;
    if k > n then go to input exhausted;
char matched: j := j + 1; k := k + 1;
loop: comment j > 0;
    if text[k] = pattern[j] then go to char matched;
    j := next[j];
    if j = 1 then go to get started;
    if j = 0 then
        begin
            j := 1; k := k + 1;
            go to get started;
        end;
    if j > 0 then go to loop;
comment text[k - m] through text[k - 1] matched;
            
          

This program will usually run faster than the naive algorithm; the worst case occurs when trying to find the pattern abab in a long string of aa's. Similar ideas can be used to speed up the program which prepares the nextnext table.

In a text-editor the patterns are usually short, so that it is most efficient to translate the pattern directly into machine-language code which implicitly contains the nextnext table (cf. [3, Hack 179] and [24]). For example, the pattern in § 1 could be compiled into the machine-language equivalent of

L0: k:=k+1k := k + 1; L1: if text[k]≠a then go to L0;\text{if } text[k] \neq a \text{ then go to L0;} k:=k+1;k := k + 1; if k>n then go to input exhausted;\text{if } k > n \text{ then go to input exhausted;} L2: if text[k]≠b then go to L1;\text{if } text[k] \neq b \text{ then go to L1;} k:=k+1;k := k + 1; L3: if text[k]≠c then go to L1;\text{if } text[k] \neq c \text{ then go to L1;} k:=k+1;k := k + 1; L4: if text[k]≠a then go to L0;\text{if } text[k] \neq a \text{ then go to L0;} k:=k+1;k := k + 1; L5: if text[k]≠b then go to L1;\text{if } text[k] \neq b \text{ then go to L1;} k:=k+1;k := k + 1; L6: if text[k]≠c then go to L1;\text{if } text[k] \neq c \text{ then go to L1;} k:=k+1;k := k + 1; L7: if text[k]≠a then go to L0;\text{if } text[k] \neq a \text{ then go to L0;} k:=k+1;k := k + 1; L8: if text[k]≠c then go to L5;\text{if } text[k] \neq c \text{ then go to L5;} k:=k+1;k := k + 1; L9: if text[k]≠a then go to L0;\text{if } text[k] \neq a \text{ then go to L0;} k:=k+1;k := k + 1; L10: if text[k]≠b then go to L1;\text{if } text[k] \neq b \text{ then go to L1;} k:=k+1;k := k + 1;

This will be slightly faster, since it essentially makes a special case for all values of j.

It is a curious fact that people often think the new algorithm will be slower than the naive one, even though it does less work. Since the new algorithm is conceptually hard to understand at first, by comparison with other algorithms of the same length, we feel somehow that a computer will have conceptual difficulties too—we expect the machine to run more slowly when it gets to such subtle instructions!

4. Extensions. So far our programs have only been concerned with finding the leftmost match. However, it is easy to see how to modify the routine so that all matches are found in turn: We can calculate the next table for the extended pattern of length m + 1 using pattern[m + 1] = "@", and then we set resume := next[m + 1] before setting next[m + 1] to −1. After finding a match and doing whatever action is desired to process that match, the sequence

j:=resume;go to loop;j := resume ; \text{go to loop;}

will restart things properly. (We assume that text has not changed in the meantime. Note that resume cannot be zero.)

Another approach would be to leave next[m + 1] untouched, never changing it to −1, and to define integer arrays head[1 : m] and link[1 : n] initially zero, and to insert the code

link[k]:=head[j];head[j]:=k;link[k] := head[j]; head[j] := k ;

at label “char matched”. The test “if j>0j > 0 then” is also removed from the program. This forms linked lists for 1≤j≤m1 \leq j \leq m of all places where the first jj characters of the pattern (but no more than jj) are matched in the input.

Still another straightforward modification will find the longest initial match of the pattern, i.e., the maximum jj such that pattern[1]…pattern[j]pattern[1] \ldots pattern[j] occurs in texttext.

In practice, the text characters are often packed into words, with say bb characters per word, and the machine architecture often makes it inconvenient to access individual characters. When efficiency for large nn is important on such machines, one alternative is to carry out bb independent searches, one for each possible alignment of the pattern’s first character in the word. These searches can treat entire words as “supercharacters”, with appropriate masking, instead of working with individual characters and unpacking them. Since the algorithm we have described does not depend on the size of the alphabet, it is well suited to this and similar alternatives.

Sometimes we want to match two or more patterns in sequence, finding an occurrence of the first followed by the second, etc.; this is easily handled by consecutive searches, and the total running time will be of order nn plus the sum of the individual pattern lengths.

We might also want to match two or more patterns in parallel, stopping as soon as any one of them is fully matched. A search of this kind could be done with multiple next and pattern tables, with one jj pointer for each; but this would make the running time knkn plus the sum of the pattern lengths, when there are kk patterns. Hopcroft and Karp have observed (unpublished) that our pattern-matching algorithm can be extended so that the running time for simultaneous searches is proportional simply to nn, plus the alphabet size times the sum of the pattern lengths. The patterns are combined into a “trie” whose nodes represent all of the initial substrings of one or more patterns, and whose branches specify the appropriate successor node as a function of the next character in the input text. For example, if there are four patterns {a b c a b, a b a b c, b c a c, b b c}\{a\ b\ c\ a\ b,\ a\ b\ a\ b\ c,\ b\ c\ a\ c,\ b\ b\ c\}, the trie is shown in Fig. 1.

nodesubstringif aaif bbif cc
0170
1aa120
2a ba\ b5103
3a b ca\ b\ c470
4a b c aa\ b\ c\ a1a b c a ba\ b\ c\ a\ bb c a cb\ c\ a\ c
5a b aa\ b\ a160
6a b a ba\ b\ a\ b510a b a b ca\ b\ a\ b\ c
7bb1108
8b cb\ c970
9b c ab\ c\ a12b c a cb\ c\ a\ c
10b bb\ b110b b cb\ b\ c

Fig. 1

Such a trie can be constructed efficiently by generalizing the idea we used to calculate next[j][j]; details and further refinements have been discussed by Aho and Corasick [2], who discovered the algorithm independently. (Note that this

Therefore if we have a mismatch when j=F8−1=20j = F_8 - 1 = 20, our algorithm might set j:=next[j]j := next[j] for the successive values 20, 12, 7, 4, 2, 1, 0 of jj. Since FkF_k is (ϕk/5)(\phi^k / \sqrt{5}) rounded to the nearest integer, it is possible to have up to ∼log⁡ϕm\sim \log_\phi m consecutive iterations of the j:=next[j]j := next[j] loop.

We shall now show that Fibonacci strings actually are the worst case, i.e., that log⁡ϕm\log_\phi m is also an upper bound. First let us consider the concept of periodicity in strings. We say that pp is a period of α\alpha if

α[i]=α[i+p]for 1≤i≤∣α∣−p.\alpha[i] = \alpha[i + p] \quad \text{for } 1 \leq i \leq |\alpha| - p.

It is easy to see that pp is a period of α\alpha if and only if

α=(α1α2)kα1\alpha = (\alpha_1 \alpha_2)^k \alpha_1

for some k≥0k \geq 0, where ∣α1α2∣=p|\alpha_1 \alpha_2| = p and α2≠ε\alpha_2 \neq \varepsilon. Equivalently, pp is a period of α\alpha if and only if

αθ1=θ2α\alpha \theta_1 = \theta_2 \alpha

for some θ1\theta_1 and θ2\theta_2 with ∣θ1∣=∣θ2∣=p|\theta_1| = |\theta_2| = p. Condition (6) implies (7) with θ1=α2α1\theta_1 = \alpha_2 \alpha_1 and θ2=α1α2\theta_2 = \alpha_1 \alpha_2. Condition (7) implies (6), for we define k=⌊∣α∣/p⌋k = \lfloor |\alpha| / p \rfloor and observe that if k>0k > 0, then α=θ2β\alpha = \theta_2 \beta implies βθ1=θ2β\beta \theta_1 = \theta_2 \beta and ⌊∣β∣/p⌋=k−1\lfloor |\beta| / p \rfloor = k - 1; hence, reasoning inductively, α=θ2kα1\alpha = \theta_2^k \alpha_1 for some α1\alpha_1 with ∣α1∣<p|\alpha_1| < p, and α1θ1=θ2α1\alpha_1 \theta_1 = \theta_2 \alpha_1. Writing θ2=α1α2\theta_2 = \alpha_1 \alpha_2 yields (6).

The relevance of periodicity to our algorithm is clear once we consider what it means to shift a pattern. If pattern[1]…pattern[j−1]=αpattern[1] \ldots pattern[j-1] = \alpha ends with pattern[1]…pattern[i−1]=βpattern[1] \ldots pattern[i-1] = \beta, we have

α=βθ1=θ2β\alpha = \beta \theta_1 = \theta_2 \beta

where ∣θ1∣=∣θ2∣=j−i|\theta_1| = |\theta_2| = j - i, so the amount of shift j−ij - i is a period of α\alpha.

The construction of i=next[j]i = next[j] in our algorithm implies further that pattern[i]pattern[i], which is the first character of θ1\theta_1, is unequal to pattern[j]pattern[j]. Let us assume that β\beta itself is subsequently shifted leaving a residue γ\gamma, so that

β=γψ1=ψ2γ\beta = \gamma \psi_1 = \psi_2 \gamma

where the first character of ψ1\psi_1 differs from that of θ1\theta_1. We shall now prove that

∣α∣>∣β∣+∣γ∣.|\alpha| > |\beta| + |\gamma|.

If ∣β∣+∣γ∣≥∣α∣|\beta| + |\gamma| \geq |\alpha|, there is an overlap of d=∣β∣+∣γ∣−∣α∣d = |\beta| + |\gamma| - |\alpha| characters between the occurrences of β\beta and γ\gamma in βθ1=α=θ2ψ2γ\beta \theta_1 = \alpha = \theta_2 \psi_2 \gamma; hence the first character of θ1\theta_1 is γ[d+1]\gamma[d+1]. Similarly there is an overlap of dd characters between the occurrences of β\beta and γ\gamma in θ2β=α=γψ1θ1\theta_2 \beta = \alpha = \gamma \psi_1 \theta_1; hence the first character of ψ1\psi_1 is β[d+1]\beta[d+1]. Since these characters are distinct, we obtain γ[d+1]≠β[d+1]\gamma[d+1] \neq \beta[d+1], contradicting (9). This establishes (10), and leads directly to the announced result:

Theorem. The number of consecutive times that j:=next[j]j := next[j] is performed, while one text character is being scanned, is at most 1+log⁡ϕm1 + \log_\phi m.

Proof. Let LrL_r be the length of the shortest string α\alpha as in the above discussion such that a sequence of rr consecutive shifts is possible. Then L1=0,L2=1L_1 = 0, L_2 = 1, and we have ∣β∣≥Lr−1,∣γ∣≥Lr−2|\beta| \geq L_{r-1}, |\gamma| \geq L_{r-2} in (10); hence L2≥Fr+1−1L_2 \geq F_{r+1} - 1 by induction on rr. Now if rr shifts occur we have m≥Fr+1≥ϕr−1m \geq F_{r+1} \geq \phi^{r-1}. □\Box

The algorithm of § 2 would run correctly in linear time even if f[j]f[j] were used instead of next[j]next[j], but the analogue of the above theorem would then be false. For example, the pattern ana^n leads to f[j]=j−1f[j] = j - 1 for 1≤j≤m1 \leq j \leq m. Therefore if we matched ama^m to the text am−1bαa^{m-1} b \alpha, using f[j]f[j] instead of next[j]next[j], the mismatch text[m]≠pattern[m]text[m] \neq pattern[m] would be followed by mm occurrences of j:=f[j]j := f[j] and m−1m - 1 redundant comparisons of text[m]text[m] with pattern[j]pattern[j], before kk is advanced to m+1m + 1.

The subject of periods in strings has several interesting algebraic properties, but a reader who is not mathematically inclined may skip to § 6 since the following material is primarily an elaboration of some additional structure related to the above theorem.

Lemma 1. If p and q are periods of α\alpha, and p+q≤∣α∣+gcd⁡(p,q)p + q \leq |\alpha| + \gcd(p, q), then gcd⁡(p,q)\gcd(p, q) is a period of α\alpha.

Proof. Let d=gcd⁡(p,q)d = \gcd(p, q), and assume without loss of generality that d<p<q=p+rd < p < q = p + r. We have α[i]=α[i+p]\alpha[i] = \alpha[i + p] for 1≤i≤∣α∣−p1 \leq i \leq |\alpha| - p and α[i]=α[i+q]\alpha[i] = \alpha[i + q] for 1≤i≤∣α∣−q1 \leq i \leq |\alpha| - q; hence α[i+r]=α[i+q]=α[i]\alpha[i + r] = \alpha[i + q] = \alpha[i] for 1+r≤i+r≤∣α∣−p1 + r \leq i + r \leq |\alpha| - p, i.e.,

α[i]=α[i+r]for 1≤i≤∣α∣−q.\alpha[i] = \alpha[i + r] \quad \text{for } 1 \leq i \leq |\alpha| - q.

Furthermore α=βθ1=θ2β\alpha = \beta \theta_1 = \theta_2 \beta where ∣θ1∣=p|\theta_1| = p, and it follows that pp and rr are periods of β\beta, where p+r≤∣β∣+d=∣β∣+gcd⁡(p,r)p + r \leq |\beta| + d = |\beta| + \gcd(p, r). By induction, dd is a period of β\beta. Since ∣β∣=∣α∣−p≥q−d≥q−r=p=∣θ1∣|\beta| = |\alpha| - p \geq q - d \geq q - r = p = |\theta_1|, the strings θ1\theta_1 and θ2\theta_2 (which have the respective forms β2β1\beta_2 \beta_1 and β1β2\beta_1 \beta_2 by (6) and (7)) are substrings of β\beta; so they also have dd as a period. The string α=(β1β2)k+1β1\alpha = (\beta_1 \beta_2)^{k+1} \beta_1 must now have dd as a period, since any characters dd positions apart are contained within β1β2\beta_1 \beta_2 or β1β1\beta_1 \beta_1.

The result of Lemma 1 but with the stronger hypothesis p+q≤∣α∣p + q \leq |\alpha| was proved by Lyndon and Schützenberger in connection with a problem about free groups [19, Lem. 4]. The weaker hypothesis in Lemma 1 turns out to give the best possible bound: If gcd⁡(p,q)<p<q\gcd(p, q) < p < q we can find a string of length p+q−gcd⁡(p,q)−1p + q - \gcd(p, q) - 1 for which gcd⁡(p,q)\gcd(p, q) is not a period. In order to see why this is so, consider first the example in Fig. 2 showing the most general strings of lengths 15 through 25 having both 11 and 15 as periods. (The strings are “most general” in the sense that any two character positions that can be different are different.)

abcdefghijklabcd abcdafghijklabcda abcdabghijklabcdab abcdabchijklabcdabc abcdabcdijklabcdabcd abcdabcdajklabcdabcd abcdabcdabkabcdabcdab abcdabcdabcabcdabc abcabcaabcabcabcabcabc aacaaacaaacaaacaaacaaacaa aaaaaaaaaaaaaaaaaaaaaaa

Fig. 2

Note that the number of degrees of freedom, i.e., the number of distinct symbols, decreases by 1 at each step. It is not difficult to prove that the number cannot decrease by more than 1 as we go from ∣α∣=n−1|\alpha| = n - 1 to ∣α∣=n|\alpha| = n, since the only new relations are α[n]=α[n−q]=α[n−p]\alpha[n] = \alpha[n-q] = \alpha[n-p]; we decrease the number of distinct symbols by one if and only if positions n−qn-q and n−pn-p contain distinct symbols in the most general string of length n−1n-1. The lemma tells us that we are left with at most gcd⁡(p,q)\gcd(p, q) symbols when the length reaches p+q−gcd⁡(p,q)p+q-\gcd(p, q); on the other hand we always have exactly pp symbols when the length is qq. Therefore each of the p−gcd⁡(p,q)p-\gcd(p, q) steps must decrease the number of symbols by 1, and the most general string of length p+q−gcd⁡(p,q)−1p+q-\gcd(p, q)-1 must have exactly gcd⁡(p,q)+1\gcd(p, q)+1 distinct symbols. In other words, the lemma gives the best possible bound.

When pp and qq are relatively prime, the strings of length p+q−2p+q-2 on two symbols, having both pp and qq as periods, satisfy a number of remarkable properties, generalizing what we have observed earlier about Fibonacci strings. Since the properties of these pathological patterns may prove useful in other investigations, we shall summarize them in the following lemma.

Lemma 2. Let the strings σ(m,n)\sigma(m, n) of length nn be defined for all relatively prime pairs of integers n≥m≥0n \geq m \geq 0 as follows:

σ(0,1)=a,σ(1,1)=b,σ(1,2)=ab;σ(m,m+n)=σ(n mod m,m)σ(m,n)σ(n,m+n)=σ(m,n)σ(n mod m,m)\begin{align*} \sigma(0, 1) &= a, \quad \sigma(1, 1) = b, \quad \sigma(1, 2) = ab; \\ \sigma(m, m+n) &= \sigma(n \bmod m, m)\sigma(m, n) \\ \sigma(n, m+n) &= \sigma(m, n)\sigma(n \bmod m, m) \end{align*}

if 0<m<n0 < m < n.

These strings satisfy the following properties: (i) σ(m,qm+r)σ(m−r,m)=σ(r,m)σ(m,qm+r),\sigma(m, qm+r)\sigma(m-r, m) = \sigma(r, m)\sigma(m, qm+r), for m>2m > 2; (ii) σ(m,n)\sigma(m, n) has period mm, for m>1m > 1; (iii) c(σ(m,n))=σ(n−m,n)c(\sigma(m, n)) = \sigma(n-m, n), for n>2n > 2. (The function c(α)c(\alpha) was defined in connection with (3) above.)

Proof. We have, for 0<m<n0 < m < n and q≥2q \geq 2,

σ(m+n,q(m+n)+m)=σ(m,m+n) σ(m+n,(q−1)(m+n)+m),σ(m+n,q(m+n)+n)=σ(n,m+n) σ(m+n,(q−1)(m+n)+n),σ(m+n,2m+n)=σ(m,m+n) σ(n mod m,m),σ(m+n,m+2n)=σ(n,m+n) σ(m,n);\begin{align*} \sigma(m+n, q(m+n)+m) &= \sigma(m, m+n)\ \sigma(m+n, (q-1)(m+n)+m), \\ \sigma(m+n, q(m+n)+n) &= \sigma(n,m+n)\ \sigma(m+n, (q-1)(m+n)+n), \\ \sigma(m+n, 2m+n) &= \sigma(m, m+n)\ \sigma(n \bmod m, m), \\ \sigma(m+n, m+2n) &= \sigma(n, m+n)\ \sigma(m, n); \end{align*}

hence, if θ1=σ(n mod m,m)\theta_1 = \sigma(n \bmod m, m) and θ2=σ(m,n)\theta_2 = \sigma(m, n) and q≥1q \geq 1,

σ(m+n,q(m+n)+m)=(θ1θ2)qθ1,σ(m+n,q(m+n)+n)=(θ2θ1)qθ2.\sigma(m+n, q(m+n)+m) = (\theta_1 \theta_2)^q \theta_1, \qquad \sigma(m+n, q(m+n)+n) = (\theta_2 \theta_1)^q \theta_2.

It follows that

σ(m+n,q(m+n)+m)σ(n,m+n)=σ(m,m+n) σ(m+n,q(m+n)+m),σ(m+n,q(m+n)+n)σ(m,m+n)=σ(n,m+n) σ(m+n,q(m+n)+n),\begin{align*} \sigma(m+n, q(m+n)+m)\sigma(n, m+n) &= \sigma(m, m+n)\ \sigma(m+n, q(m+n)+m), \\ \sigma(m+n, q(m+n)+n)\sigma(m, m+n) &= \sigma(n, m+n)\ \sigma(m+n, q(m+n)+n), \end{align*}

which combine to prove (i). Property (ii) also follows immediately from (12), except for the case m=2,n=2q+1,σ(2,2q+1)=(ab)qam = 2, n = 2q+1, \sigma(2, 2q+1) = (ab)^q a, which may be verified directly. Finally, it suffices to verify property (iii) for 0<m<12n0 < m < \frac{1}{2} n, since c(c(α))=αc(c(\alpha)) = \alpha; we must show that

c(σ(m,m+n))=σ(m,n) σ(n mod m,m)for 0<m<n.c(\sigma(m, m+n)) = \sigma(m, n)\ \sigma(n \bmod m, m) \quad \text{for } 0 < m < n.

When m≤2m \leq 2 this property is easily checked, and when m>2m > 2 it is equivalent by induction to

σ(m,m+n)=σ(m,n)σ(m−(nmod  m),m)for 0<m<n,m>2.\sigma(m, m+n) = \sigma(m, n) \sigma(m - (n \mod m), m) \quad \text{for } 0 < m < n, \quad m > 2.

Set nmod  m=r, [n/m]=qn \mod m = r,\ [n/m] = q, and apply property (i). □\Box

By properties (ii) and (iii) of this lemma, σ(p,p+q)\sigma(p, p+q) minus its last two characters is the string of length p+q−2p+q-2 having periods pp and qq. Note that Fibonacci strings are just a very special case, since ϕn=σ(Fn−1,Fn)\phi_n = \sigma(F_{n-1}, F_n). Another property of the σ\sigma strings appears in [15]. A completely different proof of Lemma 1 and its optimality, and a completely different definition of σ(m,n)\sigma(m, n), were given by Fine and Wilf in 1965 [7]. These strings have a long history going back at least to the astronomer Johann Bernoulli in 1772; see [25, § 2.13] and [21].

If α\alpha is any string, let P(α)P(\alpha) be its shortest period. Lemma 1 implies that all periods qq which are not multiples of P(α)P(\alpha) must be greater than ∣α∣−P(α)+gcd⁡(q,P(α))|\alpha| - P(\alpha) + \gcd(q, P(\alpha)). This is a rather strong condition in terms of the pattern matching algorithm, because of the following result.

Lemma 3. Let α=pattern[1]…pattern[j−1]\alpha = \text{pattern}[1] \ldots \text{pattern}[j-1] and let a=pattern[j]a = \text{pattern}[j]. In the pattern matching algorithm, f[j]=j−P(α)f[j] = j - P(\alpha), and next[j]=j−q\text{next}[j] = j - q, where qq is the smallest period of α\alpha which is not a period of αa\alpha a. (If no such period exists, next[j]=0\text{next}[j] = 0.) If P(α)P(\alpha) divides P(αa)P(\alpha a) and P(αa)<jP(\alpha a) < j, then P(α)=P(αa)P(\alpha) = P(\alpha a). If P(α)P(\alpha) does not divide P(αa)P(\alpha a) or if P(αa)=jP(\alpha a) = j, then q=P(α)q = P(\alpha).

Proof. The characterizations of f[j]f[j] and next[j]\text{next}[j] follow immediately from the definitions. Since every period of αa\alpha a is a period of α\alpha, the only nonobvious statement is that P(α)=P(αa)P(\alpha) = P(\alpha a) whenever P(α)P(\alpha) divides P(αa)P(\alpha a) and P(αa)≠jP(\alpha a) \neq j. Let P(α)=pP(\alpha) = p and P(αa)=mpP(\alpha a) = mp; then the (mp)(mp)th character from the right of α\alpha is aa, as is the (m−1)p(m-1)pth, …\ldots, as is the ppth; hence pp is a period of αa\alpha a. □\Box

Lemma 3 shows that the j:=next[j]j := \text{next}[j] loop will almost always terminate quickly. If P(α)=P(αa)P(\alpha) = P(\alpha a), then qq must not be a multiple of P(α)P(\alpha); hence by Lemma 1, P(α)+q≥j+1P(\alpha) + q \geq j + 1. On the other hand q>P(α)q > P(\alpha); hence q>12jq > \frac{1}{2}j and next[j]<12j\text{next}[j] < \frac{1}{2}j. In the other case q=P(α)q = P(\alpha), we had better not have qq too small, since qq will be a period in the residual pattern after shifting, and next[next[j]]\text{next}[\text{next}[j]] will be <q< q. To keep the loop running it is necessary for new small periods to keep popping up, relatively prime to the previous periods.

  1. Palindromes. One of the most outstanding unsolved questions in the theory of computational complexity is the problem of how long it takes to determine whether or not a given string of length nn belongs to a given context-free language. For many years the best upper bound for this problem was O(n3)O(n^3) in a general context-free language as n→∞n \to \infty; L. G. Valiant has recently lowered this to O(nlog⁡27)O(n^{\log_2 7}). On the other hand, the problem isn’t known to require more than order nn units of time for any particular language. This big gap between O(n)O(n) and O(n2.81)O(n^{2.81}) deserves to be closed, and hardly anyone believes that the final answer will be O(n)O(n).

Let Σ\Sigma be a finite alphabet, let Σ∗\Sigma^* denote the strings over Σ\Sigma, and let

P={ααR∣α∈Σ∗}.P = \{ \alpha \alpha^R | \alpha \in \Sigma^* \}.

Here αR\alpha^R denotes the reversal of α\alpha, i.e., (a1a2…an)R=an…a2a1(a_1 a_2 \ldots a_n)^R = a_n \ldots a_2 a_1. Each string π\pi in PP is a palindrome of even length, and conversely every even palindrome over

Σ is in P. At one time it was popularly believed that the language P∗P^* of “even palindromes starred”, namely the set of palstars π1…πn\pi_1 \ldots \pi_n where each πi\pi_i is in PP, would be impossible to recognize in O(n)O(n) steps on a random-access computer.

It isn’t especially easy to spot members of this language. For example, aabbabbaa a b b a b b a is a palstar, but its decomposition into even palindromes might not be immediately apparent; and the reader might need several minutes to decide whether or not

baabbabbaababbaababbabaaabbabbabbabbaabababbabbaabb a a b b a b b a a b a b b a a b a b b a b a a a \\ b b a b b a b b a b b a a b a b a b b a b b a a b

is in P∗P^*. We shall prove, however, that palstars can be recognized in O(n)O(n) units of time, by using their algebraic properties.

Let us say that a nonempty palstar is prime if it cannot be written as the product of two nonempty palstars. A prime palstar must be an even palindrome ααR\alpha \alpha^R but the converse does not hold. By repeated decomposition, it is easy to see that every palstar β\beta is expressible as a product β1…βt\beta_1 \ldots \beta_t of prime palstars, for some t≥0t \geq 0; what is less obvious is that such a decomposition into prime factors is unique. This “fundamental theorem of palstars” is an immediate consequence of the following basic property.

Lemma 1. A prime palstar cannot begin with another prime palstar.

Proof. Let ααR\alpha \alpha^R be a prime palstar such that ααR=ββRγ\alpha \alpha^R = \beta \beta^R \gamma for some nonempty even palindrome ββR\beta \beta^R and some γ≠ε\gamma \neq \varepsilon; furthermore, let ββR\beta \beta^R have minimum length among all such counterexamples. If ∣ββR∣>∣α∣|\beta \beta^R| > |\alpha| then ααR=ββRγ=αδγ\alpha \alpha^R = \beta \beta^R \gamma = \alpha \delta \gamma for some δ≠ε\delta \neq \varepsilon; hence αR=δγ\alpha^R = \delta \gamma, and ββR=(ββR)R=(αδ)R=δRαR=δRδγ\beta \beta^R = (\beta \beta^R)^R = (\alpha \delta)^R = \delta^R \alpha^R = \delta^R \delta \gamma, contradicting the minimality of ∣ββR∣|\beta \beta^R|. Therefore ∣ββR∣≤∣α∣|\beta \beta^R| \leq |\alpha|; hence α=ββRδ\alpha = \beta \beta^R \delta for some δ\delta, and ββRγ=ααR=ββRδδRββR\beta \beta^R \gamma = \alpha \alpha^R = \beta \beta^R \delta \delta^R \beta \beta^R. But this implies that γ\gamma is the palstar δδRββR\delta \delta^R \beta \beta^R, contradicting the primality of ααR\alpha \alpha^R. □\Box

Corollary (Left cancellation property.) If αβ\alpha \beta and α′\alpha' are palstars, so is β\beta.

Proof. Let α=α1…αr\alpha = \alpha_1 \ldots \alpha_r and αβ=β1…βs\alpha \beta = \beta_1 \ldots \beta_s be prime factorizations of α\alpha and αβ\alpha \beta. If α1…αr=β1…βr\alpha_1 \ldots \alpha_r = \beta_1 \ldots \beta_r, then β=βr+1…βs\beta = \beta_{r+1} \ldots \beta_s is a palstar. Otherwise let jj be minimal with αj≠βj\alpha_j \neq \beta_j; then αj\alpha_j begins with βj\beta_j or vice versa, contradicting Lemma 1. □\Box

Lemma 2. If α\alpha is a string of length nn, we can determine the length of the longest even palindrome β∈P\beta \in P such that α=βγ\alpha = \beta \gamma, in O(n)O(n) steps.

Proof. Apply the pattern-matching algorithm with pattern = α\alpha and text = αR\alpha^R. When k=n+1k = n + 1 the algorithm will stop with jj maximal such that pattern[1]…pattern[j−1]=text[n+2−j]…text[n]pattern[1] \ldots pattern[j-1] = text[n+2-j] \ldots text[n]. Now perform the following iteration:

while j≥3j \geq 3 and jj even do j:=f(j)j := f(j).

By the theory developed in § 3, this iteration terminates with j≥3j \geq 3 if and only if α\alpha begins with a nonempty even palindrome, and j−1j - 1 will be the length of the largest such palindrome. (Note that f[j]f[j] must be used here instead of next[j]; e.g. consider the case α=aabaab\alpha = a a b a a b. But the pattern matching process takes O(n)O(n) time even when f[j]f[j] is used.) □\Box

Theorem. Let LL be any language such that L∗L^* has the left cancellation property and such that, given any string α\alpha of length nn, we can find a nonempty β∈L\beta \in L such that α\alpha begins with β\beta or we can prove that no such β\beta exists, in O(n)O(n) steps. Then we can determine in O(n)O(n) time whether or not a given string is in L∗L^*.

Proof. Let α\alpha be any string, and suppose that the time required to test for nonempty prefixes in LL is ≤Kn\leq Kn for all large nn. We begin by testing α\alpha's initial subsequences of lengths 1,2,4,…,2k,…1, 2, 4, \ldots, 2^k, \ldots, and finally α\alpha itself, until finding a prefix in LL or until establishing that α\alpha has no such prefix. In the latter case, α\alpha is not in L∗L^*, and we have consumed at most (K+K1)+(2K+K1)+(4K+K1)+⋯+(∣α∣K+K1)<2Kn+K1log⁡2n(K + K_1) + (2K + K_1) + (4K + K_1) + \cdots + (|\alpha|K + K_1) < 2Kn + K_1 \log_2 n units of time for some constant K1K_1. But if we find a nonempty prefix β∈L\beta \in L where α=βγ\alpha = \beta \gamma, we have used at most 4∣β∣K+K(log⁡2∣β∣)4|\beta|K + K(\log_2 |\beta|) units of time so far. By the left cancellation property, α∈L∗\alpha \in L^* if and only if γ∈L∗\gamma \in L^*, and since ∣γ∣=n−∣β∣|\gamma| = n - |\beta| we can prove by induction that at most (4K+K1)n(4K + K_1)n units of time are needed to decide membership in L∗L^*, when n>0n > 0. □\Box

Corollary. P∗P^* can be recognized in O(n)O(n) time.

Note that the related language

P1∗={π∈Σ∗∣π=πR and ∣π∣≥2}∗P_1^* = \{ \pi \in \Sigma^* \mid \pi = \pi^R \text{ and } |\pi| \geq 2 \}^*

cannot be handled by the above techniques, since it contains both aaabbbba a a b b b b and aaabbbbbaa a a b b b b b a; the fundamental theorem of palstars fails with a vengeance. It is an open problem whether or not P1∗P_1^* can be recognized in O(n)O(n) time, although we suspect that it can be done.\footnote{(Note added April, 1976.) Zvi Galil and Joel Seiferas have recently resolved this conjecture affirmatively.} Once the reader has disposed of this problem, he or she is urged to tackle another language which has recently been introduced by S. A. Greibach [11], since the latter language is known to be as hard as possible; no context-free language can be harder to recognize except by a constant factor.

  1. Historical remarks. The pattern-matching algorithm of this paper was discovered in a rather interesting way. One of the authors (J. H. Morris) was implementing a text-editor for the CDC 6400 computer during the summer of 1969, and since the necessary buffering was rather complicated he sought a method that would avoid backing up the text file. Using concepts of finite automata theory as a model, he devised an algorithm equivalent to the method presented above, although his original form of presentation made it unclear that the running time was O(m+n)O(m + n). Indeed, it turned out that Morris’s routine was too complicated for other implementors of the system to understand, and he discovered several months later that gratuitous “fixes” had turned his routine into a shambles.

In a totally independent development, another author (D. E. Knuth) learned early in 1970 of S. A. Cook’s surprising theorem about two-way deterministic pushdown automata [5]. According to Cook’s theorem, any language recognizable by a two-way deterministic pushdown automaton, in any amount of time, can be recognized on a random access machine in O(n)O(n) units of time. Since D. Chester had recently shown that the set of strings beginning with an even palindrome could be recognized by such an automaton, and since Knuth couldn’t imagine how to recognize such a language in less than about n2n^2 steps on a conventional computer, Knuth laboriously went through all the steps of Cook’s construction as applied to Chester’s automaton. His plan was to “distill off” what was happening, in order to discover why the algorithm worked so efficiently. After pondering the mass of details for several hours, he finally succeeded in abstracting the mechanism which seemed to be underlying the construction, in the special case of palindromes, and he generalized it slightly to a program capable of finding the longest prefix of one given string that occurs in another.

This was the first time in Knuth’s experience that automata theory had taught him how to solve a real programming problem better than he could solve it before. He showed his results to the third author (V. R. Pratt), and Pratt modified Knuth’s data structure so that the running time was independent of the alphabet size. When Pratt described the resulting algorithm to Morris, the latter recognized it as his own, and was pleasantly surprised to learn of the O(m+n)O(m + n) time bound, which he and Pratt described in a memorandum [22]. Knuth was chagrined to learn that Morris had already discovered the algorithm, without knowing Cook’s theorem; but the theory of finite-state machines had been of use to Morris too, in his initial conceptualization of the algorithm, so it was still legitimate to conclude that automata theory had actually been helpful in this practical problem.

The idea of scanning a string without backing up while looking for a pattern, in the case of a two-letter alphabet, is implicit in the early work of Gilbert [10] dealing with comma-free codes. It also is essentially a special case of Knuth’s LR(0) parsing algorithm [16] when applied to the grammar

S→aS,for each a in the alphabet,S→α,S \rightarrow aS, \quad \text{for each } a \text{ in the alphabet}, \\ S \rightarrow \alpha,

where α\alpha is the pattern. Diethelm and Roizen [6] independently discovered the idea in 1971. Gilbert and Knuth did not discuss the preprocessing to build the next table, since they were mainly concerned with other problems, and the pre-processing algorithm given by Diethelm and Roizen was of order m2m^2. In the case of a binary (two-letter) alphabet, Diethelm and Roizen observed that the algorithm of § 3 can be improved further: we can go immediately to “char matched” after j:=next[j]j := next[j] in this case if next[j]>0next[j] > 0.

A conjecture by R. L. Rivest led Pratt to discover the log⁡ϕm\log_\phi m upper bound on pattern movements between successive input characters, and Knuth showed that this was best possible by observing that Fibonacci strings have the curious properties proved in § 5. Zvi Galil has observed that a real-time algorithm can be obtained by letting the text pointer move ahead in an appropriate manner while the jj pointer is moving down [9].

In his lectures at Berkeley, S. A. Cook had proved that P∗P^* was recognizable in O(nlog⁡n)O(n \log n) steps on a random-access machine, and Pratt improved this to O(n)O(n) using a preliminary form of the ideas in § 6. The slightly more refined theory in the present version of § 6 is joint work of Knuth and Pratt. Manacher [20] found another way to recognize palindromes in linear time, and Galil [9] showed how to improve this to real time. See also Slisenko [23].

It seemed at first that there might be a way to find the longest common substring of two given strings, in time O(m+n)O(m + n); but the algorithm of this paper does not readily support any such extension, and Knuth conjectured in 1970 that such efficiency would be impossible to achieve. An algorithm due to Karp, Miller, and Rosenberg [13] solved the problem in O((m+n)log⁡(m+n))O((m + n) \log (m + n)) steps, and this tended to support the conjecture (at least in the mind of its originator). However, Peter Weiner has recently developed a technique for solving the longest common substring problem in O(m+n)O(m + n) units of time with a fixed alphabet, using tree structures in a remarkable new way [26]. Furthermore, Weiner’s algorithm has the following interesting consequence, pointed out by E. McCreight: a text file can be processed (in linear time) so that it is possible to determine exactly how much of a pattern is necessary to identify a position in the text uniquely; as the pattern is being typed in, the system can be interrupt as soon as it “knows” what the rest of the pattern must be! Unfortunately the time and space requirements for Weiner’s algorithm grow with increasing alphabet size.

If we consider the problem of scanning finite-state languages in general, it is known [1 § 9.2] that the language defined by any regular expression of length mm is recognizable in O(mn)O(mn) units of time. When the regular expression has the form

Σ∗(α1,1+⋯+α1,s(1))Σ∗(α2,1+⋯+α2,s(2))Σ∗…Σ∗(αr,1+⋯+αr,s(r))Σ∗\Sigma^*(\alpha_{1,1} + \cdots + \alpha_{1,s(1)})\Sigma^*(\alpha_{2,1} + \cdots + \alpha_{2,s(2)})\Sigma^* \ldots \Sigma^*(\alpha_{r,1} + \cdots + \alpha_{r,s(r)})\Sigma^*

the algorithm we have discussed shows that only O(m+n)O(m + n) units of time are needed (considering Σ∗\Sigma^* as a character of length 1 in the expression). Recent work by M. J. Fischer and M. S. Paterson [8] shows that regular expressions of the form

Σ∗α1Σα2Σ…ΣαrΣ∗,\Sigma^*\alpha_1\Sigma\alpha_2\Sigma \ldots \Sigma\alpha_r\Sigma^*,

i.e., patterns with “don’t care” symbols, can be identified in O(nlog⁡mlog⁡log⁡mlog⁡q)O(n \log m \log \log m \log q) units of time, where qq is the alphabet size and m=∣α1α2…αr∣+rm = |\alpha_1\alpha_2 \ldots \alpha_r| + r. The constant of proportionality in their algorithm is extremely large, but the existence of their construction indicates that efficient new algorithms for general pattern matching problems probably remain to be discovered.

A completely different approach to pattern matching, based on hashing, has been proposed by Malcolm C. Harrison [12]. In certain applications, especially with very large text files and short patterns, Harrison’s method may be significantly faster than the character-comparing method of the present paper, on the average, although the redundancy of English makes the performance of his method unclear.

  1. Postscript: Faster pattern matching in strings.2^2 In the spring of 1974, Robert S. Boyer and J. Strother Moore and (independently) R. W. Gosper noticed that there is an even faster way to match pattern strings, by skipping more rapidly over portions of the text that cannot possibly lead to a match. Their idea was to look first at text[m]text[m], instead of text[1]text[1]. If we find that the character text[m]text[m] does not appear in the pattern at all, we can immediately shift the pattern right mm places. Thus, when the alphabet size qq is large, we need to inspect only about n/mn/m characters of the text, on the average! Furthermore if text[m]text[m] does occur in the pattern, we can shift the pattern by the minimum amount consistent with a match.

\footnotetext{ 2^2 This postscript was added by D. E. Knuth in March, 1976, because of developments which occurred after preprints of this paper were distributed.

Several interesting variations on this strategy are possible. For example, if text[m]text[m] does occur in the pattern, we might continue the search by looking at text[m−1],text[m−2]text[m-1], text[m-2], etc.; in a random file we will usually find a small value of rr such that the substring text[m−r]…text[m]text[m-r] \ldots text[m] does not appear in the pattern, so we can shift the pattern m−rm-r places. If r=⌊2log⁡qm⌋r = \lfloor 2 \log_q m \rfloor, there are more than m2m^2 possible values of text[m−r]…text[m]text[m-r] \ldots text[m], but only m−rm-r substrings of length r+1r+1 in the pattern, hence the probability is O(1/m)O(1/m) that text[m−r]…text[m]text[m-r] \ldots text[m] occurs in the pattern; If it doesn’t, we can shift the pattern right m−rm-r places; but if it does, we can determine all matches in positions <m−r<m-r in O(m)O(m) steps, shifting the pattern m−rm-r places by the method of this paper. Hence the expected number of characters examined among the first m−⌊2log⁡qm⌋m - \lfloor 2 \log_q m \rfloor is O(log⁡qm)O(\log_q m); this proves the existence of a linear worst-case algorithm which inspects O(n(log⁡qm)/m)O(n (\log_q m)/m) characters in a random text. This upper bound on the average running time applies to all patterns, and there are some patterns (e.g., ama^m or (ab)m/2(a b)^{m/2}) for which the expected number of characters examined by the algorithm is O(n/m)O(n/m).

Boyer and Moore have refined the skipping-by-mm idea in another way. Their original algorithm may be expressed as follows using our conventions:

k:=m;k := m;
while k≤n do\textbf{while } k \leq n \textbf{ do}
beginj:=m;while j>0 and text[k]=pattern[j] dobeginj:=j−1;k:=k−1;end;if j=0 thenbeginmatch found at (k);k:=k+m+1end elsek:=k+max⁡(d[text[k]],dd[j]);\begin{aligned} &\textbf{begin} \\ &\quad j := m; \\ &\quad \textbf{while } j > 0 \textbf{ and } text[k] = pattern[j] \textbf{ do} \\ &\qquad \textbf{begin} \\ &\qquad\qquad j := j-1; k := k-1; \\ &\qquad \textbf{end}; \\ &\quad \textbf{if } j = 0 \textbf{ then} \\ &\qquad \textbf{begin} \\ &\qquad\qquad \textit{match found at } (k); \\ &\qquad\qquad k := k + m + 1 \\ &\qquad \textbf{end else} \\ &\qquad k := k + \max (d[text[k]], dd[j]); \end{aligned}
end;\textbf{end};

This program calls match found at (k)(k) for all 0≤k≤n−m0 \leq k \leq n-m such that pattern[1]…pattern[m]=text[k+1]…text[k+m]pattern[1] \ldots pattern[m] = text[k+1] \ldots text[k+m]. There are two precomputed tables, namely

d[a]=min⁡{s∣s=m or (0≤s<m and pattern[m−s]=a)}d[a] = \min \{ s \mid s = m \text{ or } (0 \leq s < m \text{ and } pattern[m-s] = a) \}

for each of the qq possible characters aa, and

dd[j]=min⁡{s+m−j∣s≥1 and ((s≥1 or pattern[i−s]=pattern[i]) for j<i≤m)},dd[j] = \min \{ s + m - j \mid s \geq 1 \text{ and } ((s \geq 1 \text{ or } pattern[i-s] = pattern[i]) \text{ for } j < i \leq m) \},

for 1≤j≤m1 \leq j \leq m.

The dd table can clearly be set up in O(q+m)O(q + m) steps, and the dddd table can be precomputed in O(m)O(m) steps using a technique analogous to the method in § 2 above, as we shall see. The Boyer–Moore paper [4] contains further exposition of the algorithm, including suggestions for highly efficient implementation, and gives both theoretical and empirical analyses. In the remainder of this section we shall show how the above methods can be used to resolve some of the problems left open in [4].

First let us improve the original Boyer–Moore algorithm slightly by replacing dd[j]dd[j] by

dd′[j]=min⁡{s+m−j∣s≥1 and (s≥j or pattern[j−s]≠pattern[j])and ((s≥i or pattern[i−s]=pattern[i]) for j<i≤m)}.dd'[j] = \min \{ s + m - j \mid s \geq 1 \text{ and } (s \geq j \text{ or } pattern[j - s] \neq pattern[j]) \\ \text{and } ((s \geq i \text{ or } pattern[i - s] = pattern[i]) \text{ for } j < i \leq m) \}.

(This is analogous to using next[j]next[j] instead of f[j]f[j]; Boyer and Moore [4] credit the improvement in this case to Ben Kuipers, but they do not discuss how to determine dd′dd' efficiently.) The following program shows how the dd′dd' table can be precomputed in O(m)O(m) steps; for purposes of comparison, the program also shows how to compute dddd, which actually turns out to require slightly more operations than dd′dd':

            
              for k := 1 step 1 until m do dd[k] := dd'[k] := 2 × m - k;
j := m; t := m + 1;
while j > 0 do
begin
f[j] := t;
while t ≤ m and pattern[j] ≠ pattern[t] do
begin
dd'[t] := min (dd'[t], m - j);
t := f[t];
end;
t := t - 1; j := j - 1;
dd[t] := min (dd[t], m - j);
end;
for k := 1 step 1 until t do
begin
dd[k] := min (dd[k], m + t - k);
dd'[k] := min (dd'[k], m + t - k);
end;
            
          

In practice one would, of course, compute only dd′dd', suppressing all references to dddd. The example in Table 2 illustrates most of the subtleties of this algorithm.

Table 2
j=1234567891011
pattern[j]=badbacbacba
f[j]=101167891011111112
dd[j]=1918171615876541
dd'[j]=1918171615813128121

To prove correctness, one may show first that f[j]f[j] is analogous to the f[j]f[j] in § 2, but with right and left of the pattern reversed; namely f[m]=m+1f[m] = m + 1, and for j<mj < m we have

f[j]=min⁡{i∣j<i≤m and pattern[i+1]…pattern[m]=pattern[j+1]…pattern[m+j−i]}.f[j] = \min \{ i \mid j < i \leq m \text{ and } \\ \quad pattern[i+1] \ldots pattern[m] = pattern[j+1] \ldots pattern[m+j-i] \}.

Furthermore the final value of tt corresponds to f[0]f[0] in this definition; m−tm - t is the maximum overlap of the pattern on itself. The correctness of dd[j]dd[j] and dd′[j]dd'[j] for all jj now follows without much difficulty, by showing that the minimum value of ss in the definition of dd[j0]dd[j_0] or dd′[j0]dd'[j_0] is discovered by the algorithm when (t,j)=(j0,j0−s)(t, j) = (j_0, j_0 - s).

The Boyer–Moore algorithm and its variants can have curiously anomalous behavior in unusual circumstances. For example, the method discovers more quickly that the pattern aaaaaaaacba a a a a a a a c b does not appear in the text (ab)n(a b)^n if it suppresses the dd heuristic entirely, i.e., if d[t]d[t] is set to −∞-\infty for all tt. Likewise, dddd actually turns out to be better than dd′dd' when matching a15bcbababa^{15} b c b a b a b in (baabab)n(b a a b a b)^n, for large nn.

Boyer and Moore showed that their algorithm has quadratic behavior in the worst case; the running time can be essentially proportional to pattern length times text length, for example when the pattern ca(ba)mc a (b a)^m occurs together with the text (x2maa(ba)m)n(x^{2m} a a (b a)^m)^n. They observed that this particular example was handled in linear time when Kuiper’s improvement (dd′dd' for dddd) was made; but they left open the question of the true worst case behavior of the improved algorithm.

There are trivial cases in which the Boyer–Moore algorithm has quadratic behavior, when matching all occurrences of the pattern, for example when matching the pattern ama^m in the text ana^n. But we are probably willing to accept such behavior when there are so many matches; the crucial issue is how long the algorithm takes in the worst case to scan over a text that does not contain the pattern at all. By extending the techniques of § 5, it is possible to show that the modified Boyer–Moore algorithm is linear in such a situation:

Theorem. If the above algorithm is used with dd' replacing dd, and if the text does not contain any occurrences of the pattern, the total number of characters matched is at most 6n.

Proof. An execution of the algorithm consists of a series of stages, in which mkm_k characters are matched and then the pattern is shifted sks_k places, for k=1,2,…k = 1, 2, \ldots. We want to show that ∑mk≤6n\sum m_k \leq 6n; the proof is based on breaking this cost into three parts, two of which are trivially O(n)O(n) and the third of which is less obviously so.

Let mk′=mk−2skm'_k = m_k - 2s_k if mk>2skm_k > 2s_k; otherwise let mk′=0m'_k = 0. When mk′>0m'_k > 0, we will say that the leftmost mk′m'_k text characters matched during the kkth stage have been “tapped”. It suffices to prove that the algorithm taps characters at most 4n4n times, since ∑mk≤∑mk′+2∑sk\sum m_k \leq \sum m'_k + 2 \sum s_k and ∑sk≤n\sum s_k \leq n. Unfortunately it is possible for some characters of the text to be tapped roughly log⁡m\log m times, so we need to argue carefully that ∑mk′≤4n\sum m'_k \leq 4n.

Suppose the rightmost mk′′m''_k of the mk′m'_k text characters tapped during the kkth stage are matched again during some later stage, but the leftmost mk′−mk′′m'_k - m''_k are being matched for the last time. Clearly ∑(mk′−mk′′)≤n\sum (m_k' - m_k'') \leq n, so it remains to show that ∑mk′′≤3n\sum m_k'' \leq 3n.

Let pkp_k be the amount by which the pattern would shift after the kkth stage if the d[a]d[a] heuristic were not present (d[a]=−∞d[a] = -\infty); then pk≤skp_k \leq s_k, and pkp_k is a period of the string matched at stage kk.

Consider a value of kk such that mk′′>0m_k'' > 0, and suppose that the text characters matched during the kkth stage form the string α=α1α2\alpha = \alpha_1 \alpha_2 where ∣α∣=mk|\alpha| = m_k and ∣α2∣=mk′′+2sk|\alpha_2| = m_k'' + 2s_k; hence the text characters in α1\alpha_1 are matched for the last time. Since the pattern does not occur in the text, it must end with xαx \alpha and the text scanned so far must end with zαz \alpha, where x≠zx \neq z. At this point the algorithm will shift the pattern right sks_k positions and will enter stage k+1k + 1. We distinguish two cases: (i) The pattern length mm exceeds mk+pkm_k + p_k. Then the pattern can be written θβα\theta \beta \alpha, where ∣β∣=pk|\beta| = p_k; the last character of β\beta is xx and the last character of θ\theta is y≠xy \neq x, by definition of dd′dd'. Otherwise (ii) m≤mk+pkm \leq m_k + p_k; the pattern then has the form βα\beta \alpha, where ∣β∣≤pk≤sk|\beta| \leq p_k \leq s_k. By definition of mk′′m_k'' and the assumption that the pattern does not occur in the text, we have ∣βα∣>sk+∣α2∣|\beta \alpha| > s_k + |\alpha_2|, i.e., ∣β∣>sk−∣α1∣|\beta| > s_k - |\alpha_1|. In both cases (i) and (ii), pkp_k is a period of βα\beta \alpha.

Now consider the first subsequent stage k′k' during which the leftmost of the mk′′m_k'' text characters tapped during stage kk is matched again; we shall write k→k′k \to k' when the stages are in this relation. Suppose the mismatch occurs this time when text character z′z' fails to match pattern character x′x'. If z′z' occurs in the text within α1\alpha_1, regarding α\alpha as fixed in its stage kk position, then x′x' cannot be within βα\beta \alpha where βα\beta \alpha now occurs in the stage k′k' position of the pattern, since pkp_k is a period of βα\beta \alpha and the character pkp_k positions to the right of x′x' is a z′z' (it matches a z′z' in the text). Thus x′x' now appears within θ\theta. On the other hand, if z′z' occurs to the left of α\alpha, we must have ∣α1∣=0|\alpha_1| = 0, since the characters of α1\alpha_1 are never matched again. In either event, case (ii) above proves to be impossible. Hence case (i) always occurs when mk′′>0m_k'' > 0, and x′x' always appears within θ\theta.

To complete the argument, we shall show that ∑k→k′mk′′\sum_{k \to k'} m_k'', for all fixed k′k', is at most 3sk′3s_{k'}. Let p′=pk′p' = p_{k'} and let α′\alpha' denote the pattern matched at stage k′k'. Let k1<⋯<krk_1 < \cdots < k_r be the values of kk such that k→k′k \to k'. If ∣α′∣+p′≤m|\alpha'| + p' \leq m, let β′α′\beta'\alpha' be the rightmost p′+∣α′∣p' + |\alpha'| characters of the pattern. Otherwise let α′′\alpha'' be the leftmost ∣α′∣+p′−m|\alpha'| + p' - m characters of α′\alpha'; and let β′α′\beta'\alpha' be α′′\alpha'' followed by the pattern. Note that in both cases α′\alpha' is an initial substring of β′α′\beta'\alpha' and ∣β′∣=p′|\beta'| = p'. In both cases, the actions of the algorithm during stages k1+1k_1 + 1 through k′k' are completely known if we are given the pattern and β′\beta', and if we know z′z' and the place within β′\beta' where stage k1+1k_1 + 1 starts matching. This follows from the fact that β′\beta' by itself determines the text, so that if we match the pattern against the string z′β′β′β′…z'\beta'\beta'\beta' \ldots (starting at the specified place for stage k1+1k_1 + 1) until the algorithm first tries to match z′z' we will know the length of α′\alpha'. (If ∣α′∣<p′|\alpha'| < p' then β′\beta' begins with α′\alpha' and this statement holds trivially; otherwise, α′\alpha' begins with β′\beta' and has period p′p'; hence β′β′β′…\beta'\beta'\beta'\ldots begins with α′\alpha'.) Note that the algorithm cannot begin two different stages at exactly the same position within β′\beta', for then it would loop indefinitely, contradicting the fact that it does terminate. This property will be out key tool for proving the desired result.

Let the text strings matched during stages k1,…,krk_1, \ldots, k_r be α1,…,αr\alpha_1, \ldots, \alpha_r, and let their periods determined as in case (i) be p1,…,prp_1, \ldots, p_r respectively; we have pj<12∣αj∣p_j < \frac{1}{2}|\alpha_j| for 1≤j≤r1 \leq j \leq r. Suppose that during stage kjk_j the mismatch of xj≠zjx_j \neq z_j implies that the pattern ends with yjβjαjy_j \beta_j \alpha_j, where ∣βj∣=pj|\beta_j| = p_j. We shall prove that ∣α1∣+⋯+∣αr∣≤3p′|\alpha_1| + \cdots + |\alpha_r| \leq 3p'. First let us prove that ∣αj∣<p′|\alpha_j| < p' for all jj: We have observed that x′x' always occurs within θj\theta_j; hence yjβjαjy_j \beta_j \alpha_j occurs as a rightmost substring of x′α′x'\alpha'. If ∣αj∣≥p′|\alpha_j| \geq p' then pj+p′≤∣βjαj∣p_j + p' \leq |\beta_j \alpha_j|; hence the character pjp_j positions to the right of yjy_j in x′α′x'\alpha' is xjx_j, as is the character pj+p′p_j + p' positions to the right of yjy_j. But the character p′p' positions to the right of yjy_j in x′α′x'\alpha' is a yjy_j, since p′p' is a period of x′α′x'\alpha'; hence the character p′+pjp' + p_j positions to the right of yjy_j is also yjy_j, contradicting xj≠yjx_j \neq y_j.

Since ∣αj∣<p′|\alpha_j| < p', each string αj\alpha_j for j≥2j \geq 2 appears somewhere within β′\beta', when β′\beta' is regarded as a cyclic string, joined end-for-end. (It follows from the definition of k→k′k \to k' that zjαjz_j \alpha_j is a substring of α′\alpha' for j≥2j \geq 2.) We shall prove that the rightmost halves of these strings, namely the rightmost [12∣αj∣][\frac{1}{2}|\alpha_j|] characters as they appear in β′\beta', are disjoint. This implies that 12∣α2∣+⋯+12∣αr∣≤p′\frac{1}{2}|\alpha_2| + \cdots + \frac{1}{2}|\alpha_r| \leq p', and the proof will be complete (since ∣α1∣≤p′|\alpha_1| \leq p').

Suppose therefore that the right half of the appearance of αi\alpha_i overlaps the right half of the appearance of αj\alpha_j within β′\beta', for some i≠j≥2i \neq j \geq 2, where the rightmost character of αi\alpha_i is within αj\alpha_j. This means that the algorithm at stage kik_i begins to match characters starting within αj\alpha_j at least pjp_j characters to the right of zjz_j where zjαjz_j \alpha_j appears in β′\beta', when the text α′\alpha' is treated modulo p′p'. (Recall that pj<12∣αj∣p_j < \frac{1}{2}|\alpha_j|.) The pattern ends with xjαjx_j \alpha_j, and pjp_j is a period of xjαjx_j \alpha_j. The algorithm must work correctly when the text equals the pattern, so there must come a stage, before shifting the pattern to the right of the appearance of αj\alpha_j, where the algorithm scans left until hitting zjz_j. At this point, call it stage k′′k'', there must be a mismatch of zj≠xjz_j \neq x_j, since pjp_j or more characters have been matched. (The character pjp_j positions to the right of zjz_j is xjx_j, by periodicity.) Hence k′′<k′k'' < k'; and it follows that k′′=kik'' = k_i. (If k′′>kik'' > k_i we have ziαiz_i \alpha_i entirely contained within α′′\alpha'', but then ki→k′k_i \to k' implies that k′′=k′k'' = k'.) Now k′′=kik'' = k_i implies that zj=ziz_j = z_i and xj=xix_j = x_i. We shall obtain a contradiction by showing that the algorithm "synchronizes" its stage ki+1k_i + 1 behavior with its stage kj+1k_j + 1 behavior, modulo p′p', causing an infinite loop as remarked above. The main point is that the dd′dd' table will specify shifting the pattern pjp_j steps, so that yjy_j is brought into the position corresponding to zjz_j, in stage kik_i as well as in stage kjk_j. (Any lesser shift brings an xjx_j into position pjp_j spaces to the right of zjz_j; hence it puts yi=xjy_i = x_j into the position corresponding to zjz_j, by periodicity, contradicting xi≠yix_i \neq y_i.) The amount of shift depends on the maximum of the dd and dd′dd' entries, and the dd entry will be chosen (in either kik_i or kjk_j) if and only if zjz_j is not a character of βj\beta_j; but in this case, the dd entry will also specify the same shift both for stage kik_i and stage kjk_j.

The constant 6 in the above theorem is probably much too large, and the above proof seems to be much too long; the reader is invited to improve the theorem in either or both respects. An interesting example of the rather complex behavior possible with this algorithm occurs when the pattern is bψrb \psi_r and the text is ψraψr\psi_r a \psi_r for large rr, where

ψ0=a,ψn+1=ψnψnbψn.\psi_0 = a, \quad \psi_{n+1} = \psi_n \psi_n b \psi_n.

Corollary. The worst case running time of the Boyer–Moore algorithm with dd' replacing dd is O(n+rm)O(n + rm) character comparisons, if the pattern occurs r times in the text.

Proof. Let T(n,r)T(n, r) be the worst case running time as a function of nn and rr, when mm is fixed. The theorem implies that T(n,0)≤7nT(n, 0) \leq 7n, counting the mismatched characters as well as the matched ones. Furthermore, if r>0r > 0 and if the first appearance of the pattern ends at position n0n_0 we have T(n,r)≤7(n0−1)+m+T(n−n0+m−1,r−1)T(n, r) \leq 7(n_0 - 1) + m + T(n - n_0 + m - 1, r - 1). It follows that T(n,r)≤7n+8rm−14r.T(n, r) \leq 7n + 8rm - 14r.

When the Boyer–Moore algorithm implicitly shifts the pattern to the right, it forgets all it “knows” about characters already matched; this is why the linearity theorem is not trivial. A more complex algorithm can be envisaged, with a finite number of states corresponding to which text characters are known to match the pattern in its current position; when in state qq we fetch the character x:=text[k−t[q]]x := text[k - t[q]], then we set k:=k+s[q,x]k := k + s[q, x] and go to state q′[q,x]q'[q, x]. For example, consider the pattern a b a c b a b aa\ b\ a\ c\ b\ a\ b\ a, and the specification of t,s,t, s, and q′q' in Table 3; exactly 41 distinguishable states can arise. An asterisk (*) in that table shows where the pattern has been fully matched.

The number of states in this generalization of the Boyer–Moore algorithm can be rather large, as the example shows, but the patterns which occur most often in practice probably do not imply many states. The number of states is always less than 2m2^m, and perhaps a much smaller upper bound is possible; it is unclear which patterns of a given length lead to the most states, and it does not seem obvious that this maximum number of states is exponential in mm.

If the characters of the pattern are distinct, say a1a2…ama_1a_2 \ldots a_m, this generalization of the Boyer–Moore algorithm leads to exactly 12(m2+m)\frac{1}{2}(m^2 + m) states. (Namely, all states of the form ∙…∙ak∙…∙aj+1…am\bullet \ldots \bullet a_k \bullet \ldots \bullet a_{j+1} \ldots a_m for 0≤k<j≤m0 \leq k < j \leq m, with aka_k suppressed if k=0k = 0.) By merging several of these states we obtain the following simple algorithm, which uses a table c[x]c[x] where

c[x]={m−j,if x=aj;−1,if x∉{a1,…,am}.c[x] = \begin{cases} m - j, & \text{if } x = a_j; \\ -1, & \text{if } x \notin \{a_1, \ldots, a_m\}. \end{cases}

The algorithm works only when all pattern characters are distinct, but it improves slightly on the Boyer–Moore technique in this important special case.

j:=k:=m;j := k := m;
            
              while k ≤ n do
    begin i := c[text[k]];
        if i < 0 then j := m
        else if i = 0 then
            begin for i := 1 step 1 until m - 1 do
                if text[k - i] ≠ pattern[m - i] then go to nomatch;
                match found at (k - m);
            end
            nomatch: j := m;
        end else if i + j ≥ m then j := i else j := m;
        k := k + j;
    end;
            
          

Let us close this section by making a preliminary investigation into the question of “fastest” pattern matching in strings, i.e., optimum algorithms. What algorithm minimizes the number of text characters examined, over all conceivable algorithms for the problem we have been considering? In order to make this question nontrivial, we shall ask for the minimum average number of characters examined when finding all occurrences of the pattern in the text, where the average is taken uniformly with respect to strings of length nn over a given alphabet. (The minimum worst case number of characters examined is of no interest, since it is between n−mn - m and nn for all patterns3^3; therefore we ask for the minimum average number. It might be argued that the minimum average number, taken over random strings, is of little interest, since people rarely search in random strings; they usually search for patterns that actually appear. However, the random-string model is a reasonable approximation when we consider those stretches of text that do not contain the pattern, and the algorithm obviously must examine every character in those places where the pattern does occur.)

The case of patterns of length 2 can be solved exactly; it is somewhat surprising to find that the analysis is not completely trivial even in this case. Consider first the pattern abab where a≠ba \neq b. Let qq be the alphabet size, q≥2q \geq 2. Let f(n)f(n) denote the minimum average number of characters examined by an algorithm which finds all occurrences of the pattern in a random text of length nn; and let g(n)g(n) denote the minimum average number of characters examined in a random text of length n+1n + 1 which is known to begin with aa, not counting the examination of the known first character. These functions can be computed by the following recurrence relations:

f(0)=f(1)=g(0)=0,g(1)=1.f(0) = f(1) = g(0) = 0, \qquad g(1) = 1.
f(n)=1+min⁡1≤k≤n(1q(f(k−1)+g(n−k))+1q(g(k−1)+f(n−k))+(1−2q)(f(k−1)+f(n−k))),f(n) = 1 + \min_{1 \leq k \leq n} \left( \frac{1}{q}(f(k-1) + g(n-k)) + \frac{1}{q}(g(k-1) + f(n-k)) \right. \\ \left. + \left(1 - \frac{2}{q}\right)(f(k-1) + f(n-k)) \right),
g(n)=1+1qg(n−1)+(1−1q)f(n−1),n≥2.g(n) = 1 + \frac{1}{q}g(n-1) + \left(1 - \frac{1}{q}\right)f(n-1), \qquad n \geq 2.

The recurrence for ff follows by considering which character is examined first; the recurrence for gg follows from the fact that the second character must be examined in any case, so it can be examined first without loss of efficiency. It can be shown that the minimum is always assumed for k=2k = 2; hence we obtain the closed form solution

f(n)=n(q2+q−1)q(2q−1)−(q−1)(q2+2q−1)q(2q−1)2+(1−q)nqn−3(q−1)(2q−1)2,f(n) = \frac{n(q^2 + q - 1)}{q(2q-1)} - \frac{(q-1)(q^2 + 2q - 1)}{q(2q-1)^2} + \frac{(1-q)^n}{q^{n-3}(q-1)(2q-1)^2},
g(n)=n(q2+q−1)q(2q−1)+(q−1)(q2−3q+1)q(2q−1)2−(1−q)nqn−2(2q−1)2,n≥1.g(n) = \frac{n(q^2 + q - 1)}{q(2q-1)} + \frac{(q-1)(q^2 - 3q + 1)}{q(2q-1)^2} - \frac{(1-q)^n}{q^{n-2}(2q-1)^2}, \qquad n \geq 1.

(To prove that these functions satisfy the stated recurrences reduces to showing that the minimum of

(1−qq)k−1+(1−qq)n−k\left( \frac{1-q}{q} \right)^{k-1} + \left( \frac{1-q}{q} \right)^{n-k}

for 1≤k≤n1 \leq k \leq n occurs for k=2k = 2, whenever n≥2n \geq 2 and q≥2q \geq 2.)

\footnotetext{3 This is clear when we must find all occurrences of the pattern; R. L. Rivest has recently proved it also for algorithms which stop after finding one occurrence. (Information Processing Letters, to appear.)}

state qknown characterst[q]s[q, x], q'[q, x]
x = ax = bx = cother x
0● ● ● ● ● ● ●00, 11, 84, 98, 0
1● ● ● ● ● ● a17, 100, 27, 107, 10
2● ● ● ● ● b a20, 37, 102, 117, 10
3● ● ● ● a b a35, 120, 45, 125, 12
4● ● ● b a b a45, 125, 120, 55, 12
5● ● ● c b a b a50, 65, 125, 125, 12
6● ● a c b a b a65, 120, 75, 125, 12
7● b a c b a b a7*5, 125, 125, 125, 12
8● ● ● ● ● ● b ●00, 28, 08, 08, 0
9● ● ● c ● ● ● ●00, 136, 144, 98, 0
10a ● ● ● ● ● ● ●00, 151, 84, 98, 0
11● ● ● c b a ● ●00, 166, 148, 08, 0
12a b a ● ● ● ● ●00, 173, 184, 98, 0
13● ● ● c ● ● ● a17, 100, 197, 107, 10
14● b ● ● ● ● ● ●00, 203, 184, 98, 0
15a ● ● ● ● ● ● a17, 100, 217, 107, 10
16● ● ● c b a ● a17, 100, 57, 107, 10
17a b a ● ● ● ● a17, 100, 227, 107, 10
18● ● ● ● b ● ● ●00, 233, 248, 08, 0
19● ● ● c ● ● b a20, 257, 107, 107, 10
20● b ● ● ● ● ● a17, 100, 267, 107, 10
21a ● ● ● ● ● b a20, 277, 102, 117, 10
22a b a ● ● ● b a20, 287, 102, 297, 10
23● ● ● ● b ● ● a17, 100, 307, 107, 10
24● b ● ● b ● ● ●00, 313, 248, 08, 0
25● ● ● c ● a b a35, 120, 55, 125, 12
26● b ● ● ● ● b a20, 327, 102, 117, 10
27a ● ● ● ● a b a35, 120, 335, 125, 12
28a b a ● ● a b a35, 120, 345, 125, 12
29a ● ● c b a ● ●00, 356, 148, 08, 0
30● ● ● ● b ● b a20, 47, 107, 107, 10
31● b ● ● b ● ● a17, 100, 367, 107, 10
32● b ● ● ● a b a35, 120, 375, 125, 12
33a ● ● ● b a b a45, 125, 120, 385, 12
34a b a ● b a b a45, 125, 12*5, 125, 12
35a ● ● c b a ● a17, 100, 387, 107, 10
36● b ● ● b ● b a20, 377, 107, 107, 10
37● b ● ● b a b a45, 125, 120, 395, 12
38a ● ● c b a b a50, 405, 125, 125, 12
39● b ● c b a b a50, 75, 125, 125, 12
40a ● a c b a b a65, 12*5, 125, 125, 12

If the pattern is a aa\ a, the recurrence for ff changes to

f(n)=1+min⁡1≤k≤n(1q(g(k−1)+g(n−k))+(1−1q)(f(k−1)+f(n−k))),n≥2;f(n) = 1 + \min_{1 \leq k \leq n} \left( \frac{1}{q}(g(k-1) + g(n-k)) + \left(1 - \frac{1}{q}\right)(f(k-1) + f(n-k)) \right), \quad n \geq 2;

but this is actually no change! Hence the following is an optimum algorithm for all patterns of length 2, in the sense of minimum average text characters inspected to find all matches in a random string:

k:=2;k := 2;
            
              while k ≤ n do
    begin c := text[k];
        if c = pattern[2] and text[k-1] = pattern[1]
            then match found at (k-2);
        while c = pattern[1] do
            begin k := k+1; c := text[k];
                if c = pattern[2] then match found at (k-2);
            end;
        k := k+2;
    end;
            
          

For patterns of length 3 the recurrence relations become more complex; they depend on more than simply the length of the strings and knowledge about characters at the boundaries. The determination of an optimum strategy in this case remains an open problem. The algorithm sketched at the beginning of this section shows that an average of O(n(log⁡m)/m)O(n(\log m)/m) bit inspections suffices over a binary alphabet. Clearly ⌊n/m⌋\lfloor n/m \rfloor is a lower bound, since the algorithm must inspect at least one bit in any block of nn consecutive bits. The pattern ama^m can be handled with O(n/m)O(n/m) bit inspections on the average; but it seems reasonable to conjecture that patterns of length mm exist for arbitrarily large mm, such that an average of at least cn(log⁡m)/mcn (\log m)/m bits must be inspected for all large nn. Here cc denotes a positive constant, independent of mm and nn.

Acknowledgment. Robert S. Boyer and J. Strother Moore suggested many important improvements to early drafts of this postscript, especially in connection with errors in the author’s first attempts at proving the linearity theorem.

高速文字列パターン照合*

DONALD E. KNUTH†, JAMES H. MORRIS, JR.‡ AND VAUGHAN R. PRATT¶

要約。ある文字列内に別の与えられた文字列が出現するすべての位置を、その文字列の長さの和に比例する実行時間で発見するアルゴリズムを提示する。比例定数は、このアルゴリズムを実用に供するのに十分小さく、またこの手続きは、より一般的ないくつかのパターン照合問題を扱うよう拡張することもできる。このアルゴリズムの理論的応用により、偶数回文の連結の集合、すなわち言語{ααR}∗\{\alpha\alpha^R\}^*が線形時間で認識可能であることを示す。平均的にはさらに高速に実行される他のアルゴリズムについても考察する。

キーワード。パターン、文字列、テキスト編集、パターン照合、トライメモリ、探索、文字列の周期、回文、最適アルゴリズム、フィボナッチ文字列、正規表現

テキスト編集プログラムでは、与えられた「パターン」文字列の出現を探すために、しばしば文字列を検索する必要がある。我々は、パターンがテキストの連続した部分文字列として現れるすべての位置、あるいは左端の位置だけを発見したい。例えば、catenaryc a t e n a r yはパターンtent e nを含むが、canaryc a n a r yを部分文字列とはみなさない。

一致するパターンを探索する明白な方法は、テキストの各開始位置で探索を試み、誤った文字が見つかった時点で探索を打ち切ることである。

References

  1. Alfred V. Aho, John E. Hopcroft and Jeffrey D. Ullman, The Design and Analysis of Computer Algorithms, Addison-Wesley, Reading, Mass., 1974.
  2. Alfred V. Aho and Margaret J. Corasick, Efficient string matching: An aid to bibliographic search, Comm. ACM, 18 (1975), pp. 333–340.
  3. M. Beeler, R. W. GOSPER AND R. SCHROEPPEL, HAKMEM, Memo No. 239, M.I.T. Artificial Intelligence Laboratory, Cambridge, Mass., 1972.
  4. Robert S. Boyer and J. Strother Moore, A fast string searching algorithm, manuscript dated December 29, 1975; Stanford Research Institute, Menlo Park, Calif., and Xerox Palo Alto Research Center, Palo Alto, Calif.
  5. S. A. Cook, Linear time simulation of deterministic two-way pushdown automata, Information Processing 71, North-Holland, Amsterdam, 1972, pp. 75–80.
  6. Pascal DiethelM and Peter Roizen, An efficient linear search for a pattern in a string, unpublished manuscript dated April, 1972; World Health Organization, Geneva, Switzerland.
  7. N. J. Fine and H. S. Wilf, Uniqueness theorems for periodic functions, Proc. Amer. Math. Soc., 16 (1965), pp. 109–114.
  8. Michael J. Fischer and Michael S. Paterson, String matching and other products, SIAM-AMS Proc., vol. 7, American Mathematical Society, Providence, R.I., 1974, pp. 113–125.
  9. Zvi Galil, On converting on-line algorithms into real-time and on real-time algorithms for string-matching and palindrome recognition, SIGACT News, 7 (1975), No. 4, pp. 26–30.
  10. E. N. Gilbert, Synchronization of binary messages, IRE Trans. Information Theory, IT-6 (1960), pp. 470–477.
  11. Sheila A. Greibach, The hardest context-free language, this Journal, 2 (1973), pp. 304–310.
  12. Malcolm C. Harrison, Implementation of the substring test by hashing, Comm. ACM, 14 (1971), pp. 777–779.
  13. Richard M. Karp, Raymond E. Miller and Arnold L. Rosenberg, Rapid identification of repeated patterns in strings, trees, and arrays, ACM Symposium on Theory of Computing, vol. 4, Association for Computing Machinery, New York, 1972, pp. 125–136.
  14. Donald E. Knuth, Fundamental Algorithms, The Art of Computer Programming, Vol. 1, Addison-Wesley, Reading, Mass., 1968; 2nd edition 1973.
  15. ———, Sequences with precisely k + 1 k-blocks, Solution to problem E2307, Amer. Math. Monthly, 79 (1972), pp. 773–774.
  16. ———, On the translation of languages from left to right, Information and Control, 8 (1965), pp. 607–639.
  17. ———, Structured programming with go to statements, Computing Surveys, 6 (1974), pp. 261–301.
  18. Donald E. Knuth, James H. Morris, Jr. and Vaughan R. Pratt, Fast pattern matching in strings, Tech. Rep. CS440, Computer Science Department, Stanford Univ., Stanford, Calif., 1974.
  19. R. C. Lyndon and M. P. Schützenberger, The equation $a^M = b^N c^P$ in a free group, Michigan Math. J., 9 (1962), pp. 289–298.
  20. Glenn Manacher, A new linear-time on-line algorithm for finding the smallest initial palindrome of a string, J. Assoc. Comput. Mach., 22 (1975), pp. 346–351.
  21. A. Markoff, Sur une question de Jean Bernoulli, Math. Ann., 19 (1882), pp. 27–36.
  22. J. H. Morris, Jr. and Vaughan R. Pratt, A linear pattern-matching algorithm, Tech. Rep. 40, Univ. of California, Berkeley, 1970.
  23. A. O. Slisenko, Recognition of palindromes by multihead Turing machines, Dokl. Steklov Math. Inst., Akad. Nauk SSSR, 129 (1973), pp. 30–202. (In Russian.)
  24. Ken Thompson, Regular expression search algorithm, Comm. ACM, 11 (1968), pp. 419–422.
  25. B. A. Venkov, Elementary Number Theory, Wolters-Noordhoff, Groningen, the Netherlands, 1970.
  26. Peter Weiner, Linear pattern matching algorithms, IEEE Symposium on Switching and Automata Theory, vol. 14, IEEE, New York, 1973, pp. 1–11.

Read off the page by a vision model reading the page images using olmOCR-2-7B-1025-FP8.

Read off the page by a vision model reading the page images using olmOCR-2-7B-1025-FP8. Translated into 日本語 by gpt-5, against glossary version 6. Translated from the English.