home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / vol_100 / 167_01 / fgrep.doc < prev    next >
Text File  |  1985-08-21  |  28KB  |  739 lines

  1. /* 
  2. HEADER:     CUG
  3. TITLE:        Parallel Pattern Matching and FGREP
  4. VERSION:    1.00
  5. DATE:        09/20/85
  6. DESCRIPTION:    "Development of algorithm used in FGREP, a full
  7.         emulation of Unix's 'fgrep' utility."
  8. KEYWORDS:    fgrep, grep, filter, UNIX, pattern-matching
  9. SYSTEM:
  10. FILENAME:    FGREP.DOC
  11. WARNINGS:
  12. CRC:        xxxx
  13. SEE-ALSO:    FGREP.C
  14. AUTHORS:    Ian Ashdown - byHeart Software
  15. COMPILERS:
  16. REFERENCES:    AUTHORS: Bell Telephone Laboratories;
  17.         TITLE:     UNIX Programmer's Manual Vol. 1, p. 166;
  18.         AUTHORS: A.V. Aho & M.J. Corasick;
  19.         TITLE:     'Efficient String Matching: An Aid to
  20.              Bibliographic Search'
  21.              Communications of the ACM
  22.              pp. 333 - 340, Vol. 18 No. 6 (June '75);
  23. ENDREF
  24. */
  25.  
  26. /*-------------------------------------------------------------*/
  27.  
  28. Ian Ashdown
  29. byHeart Software
  30. 1089 West 21st Street
  31. North Vancouver
  32. British Columbia V7P 2C6
  33. Canada
  34.  
  35. Date: April 12th, 1985
  36.  
  37.  
  38.  
  39.  
  40.  
  41.  
  42.  
  43.  
  44.  
  45.            Parallel Pattern Matching and FGREP
  46.            -----------------------------------
  47.  
  48.              by Ian Ashdown
  49.             byHeart Software
  50.  
  51. *****************************************************************
  52. *                                *
  53. * NOTE: This manuscript was published in the September 1985    *
  54. *    issue of Doctor Dobb's Journal. It may be reproduced    *
  55. *    for personal, non-commercial use only, provided that    *
  56. *    the above copyright notice is included in all copies.    *
  57. *    Copying for any other use without previously obtaining    *
  58. *    the written permission of the author is prohibited.    *
  59. *                                *
  60. *****************************************************************
  61.  
  62.  
  63.  
  64.     Preparing an index to a large technical book. Finding all
  65. references to a person in several years' worth of a company's
  66. minutes of meetings. Searching for all occurrences of a specific
  67. sequence of events in the volumes of data obtained from a
  68. scientific experiment. All of these problems and many more are
  69. examples of searching for patterns in a set of data. The question
  70. is, what is the most efficient search algorithm to use?
  71.  
  72.     For single patterns, such as searching for the phrase
  73. "binary tree" in a text file, there are two of particular
  74. interest: the Boyer-Moore Algorithm (reference 5) and the
  75. Knuth-Morris-Pratt Algorithm (reference 6). Both are fast and
  76. reasonably simple to implement. However, neither is appropriate
  77. when more than one pattern at a time must be searched for.
  78.  
  79.     One algorithm that is appropriate can be found in a paper
  80. published in the June '75 issue of "Communications of the ACM"
  81. (reference 1). Entitled "Efficient String Matching: An Aid to
  82. Bibliographic Search" and written by Alfred Aho and Margaret
  83. Corasick of Bell Laboratories, the paper presents "a simple and
  84. efficient algorithm to locate all occurrences of any of a finite
  85. number of keywords in a string of text". Without modification to
  86. the algorithm, a "keyword" can be taken to mean any pattern, and
  87. "a string of text" to mean any sequence of symbols, be it ASCII
  88. text, digitized data obtained from a video camera, or whatever.
  89.  
  90.     The algorithm has some interesting features. For
  91. instance, most pattern matching schemes must employ some form of
  92. backtracking, or rescanning of the string when an attempted match
  93. to a pattern fails or multiple patterns are to be matched. The
  94. Aho-Corasick algorithm differs in that it searches for all of the
  95. patterns in parallel. By doing so, it can process the string in
  96. one pass without any backtracking.
  97.  
  98.     The algorithm also recognizes patterns that overlap in
  99. the string. For example, if the patterns are "he", "she" and
  100. "hers", the algorithm will correctly identify matches to all
  101. three in the string "ushers".
  102.  
  103.     As to speed of execution, it is independent of the number
  104. of patterns to be matched! This perhaps surprising feature is a
  105. direct result of the patterns being searched for in parallel.
  106.  
  107.     Curiously, this algorithm has all but disappeared from
  108. the literature of computer science. Of the hundreds of textbooks
  109. and articles written since that discuss pattern matching, only a
  110. few reference the paper, and none that I know of present the
  111. Aho-Corasick algorithm itself. Unless you have access to back
  112. issues of "Communications of the ACM", you will likely never
  113. encounter it.
  114.  
  115.     On the other hand, if you work with UNIX you may have
  116. used a utility based on the algorithm: "fgrep". This useful tool
  117. searches for and displays all occurrences of a set of keywords
  118. and phrases in one or more text files. While it does not accept
  119. wildcard characters in its patterns like UNIX's more flexible
  120. "grep" utility, it does illustrate the speed of the Aho-Corasick
  121. algorithm in comparison with other pattern matching methods.
  122. Typically, "fgrep" is five to ten times faster than "grep" in
  123. searching files for fixed string patterns.
  124.  
  125.     Given its general usefulness, I thought it appropriate to
  126. reintroduce Aho and Corasick's work in this article. At the same
  127. time, it seemed a shame that "fgrep" has been restricted to the
  128. UNIX operating system. Therefore, the source code in C for a full
  129. implementation (reference 4) of "fgrep" has been included. This
  130. serves not only to demonstrate the algorithm, but also to bring
  131. the idea of a public domain UNIX one small step closer to
  132. reality.
  133.  
  134. Inside The Machine
  135. ------------------
  136.  
  137.     The Aho-Corasick algorithm consists of two phases:
  138. building a "finite state automaton" (FSA for short), then running
  139. a string of data through it, with each consecutive symbol being
  140. considered a separate input. Before analyzing these phases, a
  141. quick summary of what finite state automata are and how they work
  142. is in order. (For a more detailed discussion, reference 2 is
  143. recommended.)
  144.  
  145.     In essence, an FSA is a conceptual machine that can be in
  146. any one of a finite number of "states". It also has a set of
  147. "state transition" rules and a set of "inputs", which together
  148. with the set of states define what inputs cause the machine to
  149. change from one state to another. Finally, since a machine serves
  150. no purpose without producing some output, an FSA has one or more
  151. "terminal" states. Output is produced only when the FSA enters
  152. such states. 
  153.  
  154.     A physical example of an FSA could be a light switch.
  155. This device has two states, ON and OFF. Its inputs consist of
  156. someone pushing the switch UP or DOWN. ON is a terminal state,
  157. where the associated lamp produces visible radiation. The state
  158. transition rules can be stated in a simple truth table:
  159.  
  160.         +------------------+
  161.         |    | OFF  |  ON  |
  162.         |----|------|------|
  163.         | UP | ON   |  --  |
  164.         |DOWN| --   |  OFF |
  165.         +------------------+
  166.  
  167.     Alternatively, this could be shown as a "state transition
  168. diagram":
  169.              DOWN
  170.            +-------------+
  171.            |         |
  172.            V         |
  173.         +-----+  UP   ******
  174.         +-->| OFF |------>* ON *<---+
  175.         |     +-----+       ******    | 
  176.         |      |         |      |
  177.         | DOWN |         |  UP  |
  178.         +------+         +------+
  179.  
  180. where no state transition on a particular input (as shown in the
  181. truth table) is equivalent to a transition from a state to itself
  182. (as shown in the diagram).
  183.  
  184.     Getting ahead of ourselves for a moment, look at Figure
  185. 1a, which shows part of an FSA (the other parts are shown in
  186. Figures 1b and 1c, and will be explained shortly). By having
  187. sequences of states, it is possible for the FSA to recognize
  188. patterns in an input stream. For example, presenting the string
  189. "she" to the FSA shown in Figure 1a would cause it to change from
  190. State 0 to States 3, 4 and 5 as each input symbol of the string
  191. is processed. State 5 is a terminal state that indicates the
  192. pattern "she" was recognized.
  193.  
  194.     There are two types of FSA that can be built, one
  195. "nondeterministic" (Algorithm 1) and the other "deterministic"
  196. (Algorithm 2). The nondeterministic one (NFSA for short) uses
  197. functions called "go_to", "failure" and "output", while the
  198. deterministic FSA (DFSA) uses "move" and "output". The difference
  199. between the two is that while the NFSA can make one or more state
  200. transitions per input symbol, the DFSA makes only one. With fewer
  201. transitions to make, a DFSA can process its input in less time
  202. than an equivalent NFSA.
  203.  
  204.     The "go_to" function accepts as input parameters the
  205. current state of the NFSA and the current input symbol, and
  206. returns either a state number (the "go_to" state transition) if
  207. the input symbol matches that expected by the patterns being
  208. matched, or else a unique symbol called FAIL. The only exception
  209. to this is State 0 - "go_to(0,X)" returns a state number for all
  210. input symbols "X". If symbol "X" does not match the beginning
  211. symbol of any of the patterns, the state number returned is 0.
  212.  
  213.     The "failure" function is called whenever the "go_to"
  214. function returns FAIL. Accepting the current state as its input
  215. parameter, it always returns a state number, the "failure" state
  216. transition.
  217.  
  218.     For the DFSA, the "move" function accepts the current
  219. state number and input symbol, and always returns the next state
  220. number. There are no failure state transitions in deterministic
  221. FSA's.
  222.  
  223.     Both the NFSA and the DFSA use the "output" function. As
  224. defined in Aho and Corasick's paper, this function prints the
  225. patterns as they are matched by the FSA. However, the definition
  226. of this function can be much more general. In fact, the FSA can
  227. be implemented to execute any arbitrary set of procedures
  228. whenever it recognizes a pattern.
  229.  
  230.     FSA's as used by the Aho-Corasick algorithm can be either
  231. nondeterministic or deterministic. While a DFSA executes more
  232. quickly than its equivalent NFSA, it also requires more memory to
  233. encode its state transition tables. Which type is chosen depends
  234. upon the application and the constraints of execution speed and
  235. memory usage.
  236.  
  237.     As an example, assume a set of text patterns to be
  238. matched: "he", "she", "his" and "hers", with ASCII characters as
  239. the set of input symbols. (These have been unabashedly purloined
  240. from Aho and Corasick.) The FSA's needed to recognize these
  241. patterns are shown in Figure 1.
  242.  
  243.     Looking at the NFSA first: using as input the string
  244. "ushers", the machine is run using Algorithm 1. Starting in State
  245. 0, the first symbol from the string is "u". Since only symbols
  246. "h" and "s" lead from State 0 to other states, "go_to(0,u)"
  247. returns 0 and so the NFSA remains in State 0.
  248.  
  249.     The next symbol from the string is "s". Following Figure
  250. 1a, the NFSA makes a go_to state transition to State 3. The next
  251. symbol ("h") causes a go_to transition to State 4, and the next
  252. ("e") to State 5. There is an output defined for State 5 (Figure
  253. 1c), and so the NFSA prints "she,he", having recognized two
  254. pattern matches.
  255.  
  256.     Continuing, the next character is "r". There are no go_to
  257. transitions defined for State 5, and so "go_to(5,r)" returns
  258. FAIL. This causes "failure(5)" to return 2 (from Figure 1b),
  259. making the NFSA perform a failure transition to State 2. From
  260. here, Algorithm 1 executes "go_to(2,r)", which causes the NFSA to
  261. enter State 8.
  262.  
  263.     Finally, the last input symbol, "s", leads the NFSA to
  264. enter terminal State 9, and the output defined for this state
  265. causes "hers" to be printed. In all, Algorithm 1 recognized the
  266. patterns "he", "she" and "hers" in the string "ushers". The state
  267. transitions made can be summarized as:
  268.  
  269.         Input Symbol:     u s h e   r s
  270.         Current State:    0 0 3 4 5 2 8 9
  271.  
  272.     Turning our attention to the DFSA and using the same
  273. input string "ushers", this machine makes move state transitions
  274. in accordance with Algorithm 2 and Figure 1d. Its state
  275. transitions can be summarized as:
  276.  
  277.         Input Symbol:     u s h e r s
  278.         Current State:    0 0 3 4 5 8 9
  279.  
  280. The only difference is that the failure transition to State 2 was
  281. skipped. By being able to avoid any failure transitions,
  282. Algorithm 2 can recognize a pattern in fewer state transitions
  283. and hence less time than Algorithm 1.
  284.  
  285. Software Construction
  286. ---------------------
  287.  
  288.     Having seen how FSA's work, it is now time to see how
  289. they are built, starting with the computation of the "go_to"
  290. function by Algorithm 3.
  291.  
  292.     The "go_to" function is initially defined to return FAIL
  293. for every input symbol and every state. Each pattern is run
  294. through procedure "enter", which tries to match the pattern
  295. symbol by symbol to the existing partially-constructed "go_to"
  296. function. When a failure occurs, a new state is created and added
  297. to the "go_to" function, with the current symbol of the pattern
  298. being the input for the state transition. Thereafter, each
  299. consecutive symbol of the pattern causes a new state to be
  300. created and added.
  301.  
  302.     When the last symbol of each pattern has been processed
  303. by procedure "enter", its associated state is made a terminal
  304. state. As shown in Algorithm 3, this means that the output for
  305. the state is defined as the current pattern, which Algorithm 1
  306. will print when the NFSA is run. However, it is easy to see that
  307. the statement "output(state) = pattern" in Algorithm 3 can be
  308. rewritten to assign whatever procedures to "output(state)" that
  309. you want.
  310.  
  311.     Finally, after all of the patterns have been processed,
  312. "go_to(0,X)" is redefined to return 0 for all symbols "X" that
  313. still return FAIL.
  314.  
  315.     When Algorithm 3 completes, it has computed the "go_to"
  316. function of the FSA and also partially computed the "output"
  317. function. If you simulate Algorithm 3 by hand with "he", "she",
  318. "his" and "hers" as its patterns, you will see that the output
  319. for State 5 will be "she", not "she,he". It remains for Algorithm
  320. 4 to complete the "output" function as it computes the "failure"
  321. function.
  322.  
  323.      Let's define the "depth" of a state as the number of
  324. go_to state transitions that must be made from State 0 to reach
  325. it. For example, the depth of State 4 in Figure 1a is 2, while
  326. that of State 9 is 4. The failure state transition for all states
  327. of depth 1 is State 0. The algorithm used to compute the failure
  328. transitions for all states of depth greater than 1 can be
  329. expressed in its simplest form as:
  330.  
  331.     for each depth D do
  332.       for each state S of depth D-1 do
  333.         for each input symbol A do
  334.           if (T = go_to(S,A)) != FAIL then
  335.         begin
  336.           R = failure(S)
  337.           while go_to(R,A) == FAIL do
  338.             R = failure(R)
  339.           failure(T) = go_to(R,A)
  340.         end
  341.  
  342.     To demonstrate this using our example, let's compute one
  343. failure transition for a state of depth 2. The states of depth 1
  344. are 1 and 3. The only input symbols for which the "go_to"
  345. function does not return FAIL are "e" and "i" for State 1 and "h"
  346. for State 3. Taking State 3 for "S" and symbol "h" for "A" above,
  347. we have "T" being 4, "R" being "failure(3)", which is 0,
  348. "go_to(0,h)" returning 1, and finally "failure(4)" being set to
  349. "go_to(0,h)". The failure transition for State 4 is thus State 1.
  350.  
  351.     Algorithm 4 expands on the above algorithm in two ways.
  352. First, it uses a queue (a first-in, first-out list) to maintain
  353. the order of states by depth to be processed. Second, it accepts
  354. as input the "output" function and combines the outputs of the
  355. terminal states where appropriate. In our example, the output
  356. "he" of State 2 would be combined with the output "she" of State
  357. 5 to produce the final and correct "she,he" output for State 5.
  358.  
  359.     The "move" function is computed from the "go_to" and
  360. "failure" functions by means of Algorithm 5. Essentially, all
  361. this algorithm does is precompute all possible sequences of
  362. failure state transitions. It is also very similar to Algorithm
  363. 4. Since there is considerable redundancy between them, they can
  364. be merged to compute the "failure" and "move" functions
  365. concurrently. The result is shown as Algorithm 6.
  366.  
  367. Details, Details
  368. ----------------
  369.  
  370.     So far, the functions "go_to", "failure", "move" and
  371. "output" have been shown as something that you can simply assign
  372. outputs to for specified inputs. This assumes a much higher-level
  373. programming language than most of today's offerings. Implementing
  374. these algorithms in C, Pascal, Basic or Fortran requires some
  375. extra code and work.
  376.  
  377.     Aho and Corasick programmed their original version in
  378. Fortran. This is evident from their paper, where they suggest
  379. that the "failure" and "output" functions could be implemented as
  380. one-dimensional arrays accessed by state numbers. With a language
  381. such as C that supports complex data structures, the code for the
  382. functions can be more elegantly written by replacing the state
  383. numbers and arrays with dynamically allocated structures. Each
  384. structure can have as members pointers to linked lists of go_to
  385. and move transitions, a pointer to the appropriate failure
  386. transition, and a pointer to a character string for the output
  387. function.
  388.  
  389.     They also noted that the "go_to" and "move" functions
  390. could be implemented as two-dimensional arrays, with the number
  391. of states forming one dimension and the set of input symbols
  392. forming the other. However, this would require enormous amounts
  393. of memory for an ASCII character set and several hundred states.
  394. Their recommendation was that the go_to and move transitions for
  395. each state be implemented as linked linear lists or binary trees.
  396.  
  397.     Since the FSA will usually spend most of its time in
  398. State 0 for large input symbol sets, it is advantageous to have
  399. the "go_to" or "move" functions execute as quickly as possible
  400. for this state. Therefore, following Aho & Corasick's suggestion,
  401. a one-dimensional array can be assigned to State 0. (Note that
  402. since there are no failure transitions defined for State 0, its
  403. go_to and move transitions are one and the same.) The array is
  404. accessed directly, using the input symbol as the index, with the
  405. corresponding array entry being the state transition for that
  406. symbol.
  407.  
  408.     An improvement not covered by Aho & Corasick can be made
  409. when it realized that often several states will have the same
  410. move transitions (see Figure 1d as an example). In such cases,
  411. the state structures can be assigned a common pointer to one
  412. linked list of move transitions.
  413.  
  414.     Some applications may call for the algorithm to be coded
  415. with predefined patterns in read-only memory. Here it is usually
  416. desired to maximize the speed of execution while at the same time
  417. minimizing the code size. Aho and Ullman (reference 2) discuss a
  418. method of encoding the state transition tables that combines the
  419. compactness of linked lists with the speed of direct array
  420. access. Their method (which UNIX's "yacc" compiler-compiler
  421. utility uses to encode its parsing tables) is unfortunately too
  422. involved to discuss here. Interested readers are referred to Aho
  423. and Ullman's book for details.
  424.  
  425. Putting It All To Work
  426. ----------------------
  427.  
  428.     Implementing the Aho-Corasick algorithm is fairly
  429. straightforward, although as you can see, the code required to
  430. flesh it out to a full emulation of UNIX's "fgrep" is somewhat
  431. involved. Regardless, the work is done for you. If you want to
  432. use the algorithm in other programs, simply remove the "fgrep"
  433. shell and add whatever interface routines you require.
  434.  
  435.     An example: as an information retrieval utility that
  436. searches arbitrary text files, "fgrep" is useful but by no means
  437. complete. One useful extension would be the ability to specify
  438. Boolean operators (AND, OR and EXCLUSIVE-OR) for combinations of
  439. patterns.
  440.  
  441.     Another one: rather than simply print matched patterns
  442. for its output, the FSA can execute whatever procedures you
  443. choose to associate with the patterns as they are matched. From
  444. this you could create a macro processor, where the FSA accepts an
  445. input string, finds matches to predefined patterns, substitutes
  446. the corresponding macro expansions, and then emits the result as
  447. an output string.
  448.  
  449.     For those with RAM memory to spare (a megabyte or so),
  450. consider how fast a spelling checker that makes no disk accesses
  451. beyond initially loading itself could be made to run ... fifty
  452. thousand or more words in RAM and your text file is checked
  453. almost as fast as it can be read.
  454.  
  455.     If you come up with an original and fascinating use for
  456. the Aho-Corasick algorithm, or if you derive new utilities from
  457. "fgrep", by all means let me know about it. Better yet, donate
  458. your code to a public domain software group. That alone would
  459. repay me for the effort I put into developing the code and
  460. writing this article.
  461.  
  462. ------------------------------------------------------
  463. Algorithm 1: Nondeterministic Pattern Matching Machine
  464. ------------------------------------------------------
  465.  
  466. Input:  A string of symbols and a pattern-matching machine
  467.     consisting of functions "go_to", "failure" and "output".
  468. Output: Matched patterns.
  469.  
  470. begin
  471.   state = 0
  472.   while there are more symbols in the string do
  473.     begin
  474.       a = next symbol in the string
  475.       while go_to(state,a) == FAIL do
  476.     state = failure(state)
  477.       state = go_to(state,a)
  478.       if output(state) != NULL then
  479.     print output(state)
  480.     end
  481. end
  482.  
  483. ---------------------------------------------------
  484. Algorithm 2: Deterministic Pattern Matching Machine
  485. ---------------------------------------------------
  486.  
  487. Input:  A string of symbols and a pattern-matching machine
  488.     consisting of functions "move" and "output".
  489. Output: Matched patterns.
  490.  
  491. begin
  492.   state = 0
  493.   while there are more symbols in the string do
  494.     begin
  495.       a = next symbol in the string
  496.       state = move(state,a)
  497.       if output(state) != NULL then
  498.     print output(state)
  499.     end
  500. end
  501.  
  502. ---------------------------------------------
  503. Algorithm 3: Computation of Go_to Transitions
  504. ---------------------------------------------
  505.  
  506. Input:  Array of patterns {y[1],y[2] .... y[k]} where each
  507.     pattern is a string (one-dimensional array) of symbols.
  508.     (Function "go_to(s,a)" is defined to return FAIL for all
  509.     states "s" and all input symbols "a" until defined
  510.     otherwise by procedure "enter".)
  511. Output: Function "go_to" and partially computed function
  512.     "output". 
  513.  
  514. begin
  515.   newstate = 0
  516.   for i = 1 to i = k do
  517.     enter(y[i])
  518.   for each symbol a do
  519.     if go_to(0,a) == FAIL then
  520.       go_to(0,a) = 0
  521. end
  522.  
  523. procedure enter(pattern)    /* "pattern" is an array of */
  524. begin                /* "m" symbols */
  525.   state = 0
  526.   j = 1
  527.   while go_to(state,pattern[j]) != FAIL do
  528.     begin
  529.       state = go_to(state,pattern[j])
  530.       j = j + 1
  531.     end
  532.   for p = j to p = m do
  533.     begin
  534.       newstate = newstate + 1
  535.       output(newstate) = NULL
  536.       go_to(state,pattern[p]) = newstate
  537.       state = newstate
  538.     end
  539.   output(state) = pattern
  540. end
  541.  
  542. -----------------------------------------------
  543. Algorithm 4: Computation of Failure Transitions
  544. -----------------------------------------------
  545.  
  546. Input:  Functions "go_to" and "output" from Algorithm 3.
  547. Output:    Functions "failure" and "output". 
  548.  
  549. begin
  550.   initialize queue
  551.   for each symbol a do
  552.     if (r = go_to(0,a)) != 0 then
  553.       begin
  554.     add r to tail of queue
  555.     failure(r) = 0
  556.       end
  557.   while queue is not empty do
  558.     begin
  559.       s = head of queue
  560.       remove head of queue
  561.       for each symbol a do
  562.     if (t = go_to(s,a)) != FAIL then
  563.       begin
  564.         add t to tail of queue
  565.         r = failure(s)
  566.         while go_to(r,a) == FAIL do
  567.           r = failure(r)
  568.         failure(t) = go_to(r,a)
  569.         output(t) = output(t) + output(failure(t))
  570.       end
  571.     end
  572. end
  573.  
  574. --------------------------------------------
  575. Algorithm 5: Computation of Move Transitions
  576. --------------------------------------------
  577.  
  578. Input:    Function "go_to" from Algorithm 3 and function "failure"
  579.     from Algorithm 4.
  580. Output:    Function "move".
  581.  
  582. begin
  583.   initialize queue
  584.   for each symbol a do
  585.     begin
  586.       move(0,a) = go_to(0,a)
  587.       if (r = go_to(0,a)) != 0 then
  588.     add r to tail of queue
  589.     end
  590.   while queue is not empty do
  591.     begin
  592.       s = head of queue
  593.       remove head of queue
  594.       for each symbol a do
  595.     if (t = go_to(s,a)) != FAIL then
  596.       begin
  597.         add t to tail of queue
  598.         move(s,a) = t
  599.       end
  600.     else
  601.       move(s,a) = move(failure(s),a)
  602.     end
  603. end
  604.  
  605. ---------------------------------------------------------------
  606. Algorithm 6: Merged Computation of Failure and Move Transitions
  607. ---------------------------------------------------------------
  608.  
  609. Input:  Functions "go_to" and "output" from Algorithm 3.
  610. Output:    Function "move".
  611.  
  612. begin
  613.   initialize queue
  614.   for each symbol a do
  615.     begin
  616.       move(0,a) = go_to(0,a)
  617.       if (r = go_to(0,a)) != 0 then
  618.     begin
  619.       add r to tail of queue
  620.       failure(r) = 0
  621.     end
  622.     end
  623.   while queue is not empty do
  624.     begin
  625.       s = head of queue
  626.       remove head of queue
  627.       for each symbol a do
  628.     if (t = go_to(s,a)) != FAIL then
  629.       begin
  630.         add t to tail of queue
  631.         r = failure(s)
  632.         while go_to(r,a) == FAIL do
  633.           r = failure(r)
  634.         failure(t) = go_to(r,a)
  635.         output(t) = output(t) + output(failure(t))
  636.         move(s,a) = t
  637.       end
  638.     else
  639.       move(s,a) = move(failure(s),a)
  640.     end
  641. end
  642.  
  643. =================================================================
  644.  
  645. Figure 1: Pattern Matching Machine
  646. ----------------------------------
  647.  
  648. !{h,s} (any symbol but "h" or "s")
  649. +-----+
  650. |     V
  651. |   +---+  h  +---+  e  *****  r  +---+  s  *****
  652. +---| 0 |---->| 1 |---->* 2 *---->| 8 |---->* 9 *
  653.     +-+-+     +-+-+     *****     +---+     *****
  654.       |         |
  655.       |         |    i  +---+  s  *****
  656.       |         +------>| 6 |---->* 7 *
  657.       |                 +---+     *****
  658.       |
  659.       |    s  +---+  h  +---+  e  *****
  660.       +------>| 3 |---->| 4 |---->* 5 *
  661.               +---+     +---+     *****
  662.  
  663.  
  664.         (a) Go_to function
  665.  
  666.  
  667. Current State:    1  2  3  4  5  6  7  8  9
  668. Failure State:    0  0  0  1  2  0  3  0  3
  669.  
  670.  
  671.          (b) Failure function
  672.  
  673.  
  674. Current        Output:
  675. State:
  676.  
  677.   0         --
  678.   1         --
  679.   2        {he}
  680.   3         --
  681.   4         --
  682.   5        {she,he}
  683.   6         --
  684.   7        {his}
  685.   8         --
  686.   9        {hers}
  687.  
  688.  
  689.         (c) Output function
  690.  
  691. Current      Input     Next    Current      Input     Next
  692. State:    Symbol:   State:    State:    Symbol:   State:
  693.  
  694.   0         h         1          4         e         5
  695.             s         3                    i         6
  696.             .         0                    h         1
  697.   1         e         2                    s         3
  698.             i         6                    .         0
  699.             h         1          6         s         7
  700.             s         3                    h         1
  701.             .         0                    .         0
  702.   2,5       r         8          8         s         9
  703.             h         1                    h         1
  704.             s         3                    .         0
  705.             .         0
  706.   3,7,9     h         4
  707.             s         3
  708.             .         0
  709.  
  710.   where "." represents any symbol not included in the list of
  711.   symbols for the indicated state(s).
  712.  
  713.  
  714.         (d) Move function
  715.  
  716. =================================================================
  717.  
  718. References:
  719.  
  720. 1. Aho, A.V. and Corasick, M.J. [1975]. Efficient String
  721.    Matching: An Aid to Bibliographic Search. CACM 18:6 (June),
  722.    pp.333-340.
  723. 2. Aho, A.V. and Ullman, J.D. [1977]. Principles of Compiler
  724.    Design. Addison Wesley, pp.73-124.
  725. 3. Aoe, J., Yamamoto, Y. and Shimada, R. [1984]. A Method for
  726.    Improving String Pattern Matching Machines. IEEE Transactions
  727.    On Software Engineering, SE-10:1 (January), pp.116-120.
  728. 4. Bell Telephone Laboratories [1983], UNIX Programmer's Manual
  729.    Volume 1, Holt, Rinehart and Winston, p.70-71.
  730. 5. Boyer, R.S. and Moore, J.S. [1977]. A Fast String Searching
  731.    Algorithm. CACM 20:10 (October), pp.762-772.
  732. 6. Knuth, D.E., Morris, J.H. and Pratt, V.R. [1977]. Fast Pattern
  733.    Matching in Strings, SIAM J. Comp. 6:2 (June), pp.323-350.
  734.  
  735.  
  736.                  - End -
  737. 
  738.   where "." represents any symbol not included in the list of
  739.   symbols for the indicated sta