100% found this document useful (2 votes)
40 views

PDF String Algorithms in C: Efficient Text Representation and Search 1st Edition Thomas Mailund download

Representation

Uploaded by

zandtpardiht
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
100% found this document useful (2 votes)
40 views

PDF String Algorithms in C: Efficient Text Representation and Search 1st Edition Thomas Mailund download

Representation

Uploaded by

zandtpardiht
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 55

Download the Full Version of textbook for Fast Typing at textbookfull.

com

String Algorithms in C: Efficient Text


Representation and Search 1st Edition Thomas
Mailund

https://textbookfull.com/product/string-algorithms-in-c-
efficient-text-representation-and-search-1st-edition-thomas-
mailund/

OR CLICK BUTTON

DOWNLOAD NOW

Download More textbook Instantly Today - Get Yours Now at textbookfull.com


Recommended digital products (PDF, EPUB, MOBI) that
you can download immediately if you are interested.

The Joys of Hashing: Hash Table Programming with C 1st


Edition Thomas Mailund

https://textbookfull.com/product/the-joys-of-hashing-hash-table-
programming-with-c-1st-edition-thomas-mailund/

textboxfull.com

Pointers in C Programming A Modern Approach to Memory


Management Recursive Data Structures Strings and Arrays
Thomas Mailund
https://textbookfull.com/product/pointers-in-c-programming-a-modern-
approach-to-memory-management-recursive-data-structures-strings-and-
arrays-thomas-mailund/
textboxfull.com

Domain Specific Languages in R Advanced Statistical


Programming 1st Edition Thomas Mailund

https://textbookfull.com/product/domain-specific-languages-in-r-
advanced-statistical-programming-1st-edition-thomas-mailund/

textboxfull.com

Domain Specific Languages in R Advanced Statistical


Programming 1st Edition Thomas Mailund

https://textbookfull.com/product/domain-specific-languages-in-r-
advanced-statistical-programming-1st-edition-thomas-mailund-2/

textboxfull.com
C Data Structures and Algorithms Explore the possibilities
of C for developing a variety of efficient applications
Marcin Jamro
https://textbookfull.com/product/c-data-structures-and-algorithms-
explore-the-possibilities-of-c-for-developing-a-variety-of-efficient-
applications-marcin-jamro/
textboxfull.com

Functional Data Structures in R: Advanced Statistical


Programming in R Thomas Mailund

https://textbookfull.com/product/functional-data-structures-in-r-
advanced-statistical-programming-in-r-thomas-mailund/

textboxfull.com

Metaprogramming in R: Advanced Statistical Programming for


Data Science, Analysis and Finance 1st Edition Thomas
Mailund
https://textbookfull.com/product/metaprogramming-in-r-advanced-
statistical-programming-for-data-science-analysis-and-finance-1st-
edition-thomas-mailund/
textboxfull.com

C# Data Structures and Algorithms: Harness the power of C#


to build a diverse range of efficient applications, 2nd
Edition Marcin Jamro
https://textbookfull.com/product/c-data-structures-and-algorithms-
harness-the-power-of-c-to-build-a-diverse-range-of-efficient-
applications-2nd-edition-marcin-jamro/
textboxfull.com

Introducing Markdown and Pandoc Using Markup Language and


Document Converter 1st Edition Thomas Mailund

https://textbookfull.com/product/introducing-markdown-and-pandoc-
using-markup-language-and-document-converter-1st-edition-thomas-
mailund/
textboxfull.com
String
Algorithms in C
Ef f icient Text Representation and Search

Thomas Mailund

WOW! eBook
www.wowebook.org
String Algorithms in C
Efficient Text Representation
and Search

Thomas Mailund

WOW! eBook
www.wowebook.org
String Algorithms in C: Efficient Text Representation and Search
Thomas Mailund
Aarhus N, Denmark

ISBN-13 (pbk): 978-1-4842-5919-1 ISBN-13 (electronic): 978-1-4842-5920-7


https://doi.org/10.1007/978-1-4842-5920-7

Copyright © 2020 by Thomas Mailund


This work is subject to copyright. All rights are reserved by the Publisher, whether the whole or part of the
material is concerned, specifically the rights of translation, reprinting, reuse of illustrations, recitation,
broadcasting, reproduction on microfilms or in any other physical way, and transmission or information
storage and retrieval, electronic adaptation, computer software, or by similar or dissimilar methodology now
known or hereafter developed.
Trademarked names, logos, and images may appear in this book. Rather than use a trademark symbol with
every occurrence of a trademarked name, logo, or image we use the names, logos, and images only in an
editorial fashion and to the benefit of the trademark owner, with no intention of infringement of the
trademark.
The use in this publication of trade names, trademarks, service marks, and similar terms, even if they are not
identified as such, is not to be taken as an expression of opinion as to whether or not they are subject to
proprietary rights.
While the advice and information in this book are believed to be true and accurate at the date of publication,
neither the authors nor the editors nor the publisher can accept any legal responsibility for any errors or
omissions that may be made. The publisher makes no warranty, express or implied, with respect to the
material contained herein.
Managing Director, Apress Media LLC: Welmoed Spahr
Acquisitions Editor: Steve Anglin
Development Editor: Matthew Moodie
Coordinating Editor: Mark Powers
Cover designed by eStudioCalamar
Cover image by Jonathan J Castellon on Unsplash (www.unsplash.com)
Distributed to the book trade worldwide by Springer Science+Business Media New York, 233 Spring Street,
6th Floor, New York, NY 10013. Phone 1-800-SPRINGER, fax (201) 348-4505, e-mail orders-ny@springer-
sbm.com, or visit www.springeronline.com. Apress Media, LLC is a California LLC and the sole member
(owner) is Springer Science + Business Media Finance Inc (SSBM Finance Inc). SSBM Finance Inc is a
Delaware corporation.
For information on translations, please e-mail booktranslations@springernature.com; for reprint,
paperback, or audio rights, please e-mail bookpermissions@springernature.com.
Apress titles may be purchased in bulk for academic, corporate, or promotional use. eBook versions and
licenses are also available for most titles. For more information, reference our Print and eBook Bulk Sales
web page at http://www.apress.com/bulk-sales.
Any source code or other supplementary material referenced by the author in this book is available to
readers on GitHub via the book’s product page, located at www.apress.com/9781484259191. For more
detailed information, please visit http://www.apress.com/source-code.
Printed on acid-free paper

WOW! eBook
www.wowebook.org
Table of Contents
About the Author���������������������������������������������������������������������������������������������������� vii

About the Technical Reviewer��������������������������������������������������������������������������������� ix

Chapter 1: Introduction�������������������������������������������������������������������������������������������� 1
Notation and conventions������������������������������������������������������������������������������������������������������������� 1
Graphical notation������������������������������������������������������������������������������������������������������������������������� 2
Code conventions�������������������������������������������������������������������������������������������������������������������������� 3
Reporting a sequence of results��������������������������������������������������������������������������������������������������� 5

Chapter 2: Classical algorithms for exact search�������������������������������������������������� 11


Naïve algorithm��������������������������������������������������������������������������������������������������������������������������� 12
Border array and border search�������������������������������������������������������������������������������������������������� 14
Borders and border arrays���������������������������������������������������������������������������������������������������������� 15
Exact search using borders�������������������������������������������������������������������������������������������������������� 18
Knuth-Morris-Pratt���������������������������������������������������������������������������������������������������������������������� 21
Boyer-Moore-Horspool���������������������������������������������������������������������������������������������������������������� 28
Extended rightmost table������������������������������������������������������������������������������������������������������������ 35
Boyer-Moore������������������������������������������������������������������������������������������������������������������������������� 38
Jump rule one����������������������������������������������������������������������������������������������������������������������� 40
Second jump table����������������������������������������������������������������������������������������������������������������� 53
Combining the jump rules����������������������������������������������������������������������������������������������������� 55
Aho-Corasick������������������������������������������������������������������������������������������������������������������������������� 58
Tries��������������������������������������������������������������������������������������������������������������������������������������� 58
Comparisons������������������������������������������������������������������������������������������������������������������������������� 83

iii

WOW! eBook
www.wowebook.org
Table of Contents

Chapter 3: Suffix trees�������������������������������������������������������������������������������������������� 87


Compacted trie and suffix representation����������������������������������������������������������������������������������� 88
Naïve construction algorithm������������������������������������������������������������������������������������������������������ 93
Suffix trees and the SA and LCP arrays������������������������������������������������������������������������������������ 101
Constructing the SA and LCP arrays������������������������������������������������������������������������������������ 101
Constructing the suffix tree from the SA and LCP arrays���������������������������������������������������� 104
McCreight’s algorithm��������������������������������������������������������������������������������������������������������������� 110
Searching with suffix trees������������������������������������������������������������������������������������������������������� 123
Leaf iterators����������������������������������������������������������������������������������������������������������������������� 125
Comparisons����������������������������������������������������������������������������������������������������������������������������� 131

Chapter 4: Suffix arrays���������������������������������������������������������������������������������������� 139


Constructing suffix arrays��������������������������������������������������������������������������������������������������������� 142
Trivial constructions—Comparison-based sorting�������������������������������������������������������������� 142
The skew algorithm������������������������������������������������������������������������������������������������������������� 145
The SA-IS algorithm������������������������������������������������������������������������������������������������������������������ 167
Remapping�������������������������������������������������������������������������������������������������������������������������� 176
Implementing the algorithm������������������������������������������������������������������������������������������������ 179
Memory reduction��������������������������������������������������������������������������������������������������������������� 193
Searching using suffix arrays��������������������������������������������������������������������������������������������������� 206
Binary search���������������������������������������������������������������������������������������������������������������������� 206
Burrows-Wheeler transform–based search������������������������������������������������������������������������ 214
Getting the longest common prefix (LCP) array������������������������������������������������������������������������ 221
Comparisons����������������������������������������������������������������������������������������������������������������������������� 225

Chapter 5: Approximate search���������������������������������������������������������������������������� 235


Local alignment and CIGAR notation����������������������������������������������������������������������������������������� 235
Brute force approach���������������������������������������������������������������������������������������������������������������� 238
Building an edit cloud���������������������������������������������������������������������������������������������������������� 238
Suffix trees�������������������������������������������������������������������������������������������������������������������������������� 250
The Li-Durbin algorithm������������������������������������������������������������������������������������������������������������ 258
Comparisons����������������������������������������������������������������������������������������������������������������������������� 269

iv

WOW! eBook
www.wowebook.org
Table of Contents

Chapter 6: Conclusions����������������������������������������������������������������������������������������� 273

Appendix: Fundamental data structures�������������������������������������������������������������� 275


V ectors�������������������������������������������������������������������������������������������������������������������������������������� 275
Lists������������������������������������������������������������������������������������������������������������������������������������������ 281
Queues�������������������������������������������������������������������������������������������������������������������������������������� 282

Index��������������������������������������������������������������������������������������������������������������������� 287

WOW! eBook
www.wowebook.org
About the Author
Thomas Mailund is an associate professor in bioinformatics at Aarhus University,
Denmark. He has a background in math and computer science. For the past decade,
his main focus has been on genetics and evolutionary studies, particularly comparative
genomics, speciation, and gene flow between emerging species. He has published R
Data Science Quick Reference, The Joys of Hashing, Domain-Specific Languages in R,
Beginning Data Science in R, Functional Programming in R, and Metaprogramming in R,
all from Apress, as well as other books.

vii

WOW! eBook
www.wowebook.org
About the Technical Reviewer
Jason Whitehorn is an experienced entrepreneur and
software developer and has helped many companies
automate and enhance their business solutions through data
synchronization, SaaS architecture, and machine learning.
Jason obtained his Bachelor of Science in Computer Science
from Arkansas State University, but he traces his passion
for development back many years before then, having first
taught himself to program BASIC on his family’s computer
while still in middle school.
When he’s not mentoring and helping his team at work,
writing, or pursuing one of his many side projects, Jason enjoys spending time with his
wife and four children and living in the Tulsa, Oklahoma region. More information about
Jason can be found on his website: ­https://jason.whitehorn.us.

ix

WOW! eBook
www.wowebook.org
CHAPTER 1

Introduction
Algorithms operating on strings are fundamental to many computer programs, and in
particular searching for one string in another is the core of many algorithms. An example
is searching for a word in a text document, where we want to know everywhere it occurs.
This search can be exact, meaning that we are looking for the positions where the word
occurs verbatim, or approximative, where we allow for some spelling mistakes.
This book will teach you fundamental algorithms and data structures for exact and
approximative search. The goal of the book is not to cover the theory behind the material
in great detail. However, we will see theoretical considerations where relevant. The
purpose of the book is to give you examples of how the algorithms can be implemented.
For every algorithm and data structure in the book, I will present working C code and
nowhere will I use pseudocode. When I argue for the correctness and running time of
algorithms, I do so intentionally informal. I aim at giving you an idea about why the
algorithms solve a specific problem in a given time, but I will not mathematically prove so.
You can copy all the algorithms and data structures in this book from the pages,
but they are also available in a library on GitHub: https://github.com/mailund/
stralg. You can download and link against the library or copy snippets of code into
your own projects. On GitHub you can also find all the programs I have used for time
measurement experiments so you can compare the algorithm’s performance on your
own machine and in your own runtime environment.

N
 otation and conventions
Unless otherwise stated, we use x, y, and p to refer to strings and i, j, k, l, and h to denote
indices. We use 𝜖 to denote the empty string. We use a, b, and c for single characters. As
in C, we do not distinguish between strings and pointers to a sequence of characters.
Since the book is about algorithms in C, the notation we use matches that which is
used for strings, pointers, and arrays in C. Arrays and strings are indexed from zero,

1
© Thomas Mailund 2020
T. Mailund, String Algorithms in C, https://doi.org/10.1007/978-1-4842-5920-7_1

WOW! eBook
www.wowebook.org
Chapter 1 Introduction

that is, A[0] is the first value in array A (and x[0] is the first character in string x). The ith
character in a string is at index i − 1.
When we refer to a substring, we define it using two indices, i and j, i ≤ j, and we
write x[i, j] for the substring. The first index is included and the second is not, that is,
x[i, j] = x[i]x[i + 1] · · · x[ j − 1]. If a string has length n, then the substring x[0, n] is the full
string. If we have a character a and a string x, then ax denotes the string that has a as its
first character and is then followed by the string x. We use ak to denote a sequence of as
of length k. The string a3 x has a as its first three characters and is then followed by x.
A substring that starts at index 0, x[0, i], is a prefix of the string, and it is a proper prefix
if it is neither the empty string x[0, 0] = 𝜖 nor the full string x[0, n]. A substring that ends
in n, x[i, n], is a suffix, and it is a proper suffix if it is neither the empty string nor the full
string. We will sometimes use x[i, ] for this suffix.
We use $ to denote a sentinel in a string, that is, it is a character that is not found in
the rest of the string. It is typically placed at the end of the string. The zero-terminated
C strings have the zero byte as their termination sentinel, and unless otherwise stated,
$ refers to that. All C strings x have a zero sentinel at index n if the string has length n,
x = x[0]x[1] · · · x[n − 1]0. For some algorithms, the sentinel is essential; in others, it is not.
We will leave it out of the notation when a sentinel isn’t needed for an algorithm, but
naturally include the sentinel when it is necessary.

G
 raphical notation
Most data structures and algorithmic ideas are simpler to grasp if we use drawings to
capture the structure of strings rather than textual notation. Because of this, I have chosen to
provide more figures in this book than you will typically see in a book on algorithms. I hope
you will appreciate it. If there is anything you find unclear about an algorithm, I suggest you
try to draw key strings yourself and work out the properties you have problems with.
In figures, we represent strings as rectangles. We show indices into a string as arrows
pointing to the index in the string; see Figure 1-1. In this notation, we do not distinguish
between pointers and indices. If a variable is an index j and it points into x, then what
it points to is x[ j], naturally. If the variable is a pointer, y, then what it points to is ∗y.
Whether we are working with pointers or indices should be clear from the context. It will
undoubtedly be clear from the C implementations. We represent substrings by boxes of
a different color inside the original string-rectangle. If we specify the indices defining the
substring, we include their start and stop index (where the stop index points one after
the end of the substring).
2

WOW! eBook
www.wowebook.org
Chapter 1 Introduction

Figure 1-1. Graphical string notation


When we compare two strings, we imagine that we align the boxes representing
them, so the parts we are comparing are on top of each other. For example, if we
compare the character at index j in a string x with the character at index i in another
string p, then we draw a box representing x over a box representing p, and we draw
pointers for the two indices; see Figure 1-2. Since we are comparing the characters in the
two indices, the two pointers are pointing at each other. Conceptually, we imagine that p
is aligned under x starting at position j − i.

Figure 1-2. Graphical notation for comparing indices in two different strings

C
 ode conventions
There is a trade-off between long variables and type names and then the line within a
book. In many cases, I have had to use an indentation that you might not be used to. In
function prototypes and function definitions, I will generally write with one variable per
line, indented under the function return type and name, for example:

void compute_z_array(
    const unsigned char *x,
    uint32_t n,
    uint32_t *Z
);
3

WOW! eBook
www.wowebook.org
Chapter 1 Introduction

void compute_reverse_z_array(
    const unsigned char *x,
    uint32_t m,
    uint32_t *Z
);

If a return type name is long, I will put it on a separate line:

static inline uint32_t


edge_length(struct suffix_tree_node *n) {
    return range_length(n->range);
}
struct suffix_tree *
mccreight_suffix_tree(
    const unsigned char *string
);
struct suffix_tree *
lcp_suffix_tree(
    const unsigned char *string,
    uint32_t *sa,
    uint32_t *lcp
);
struct suffix_tree_node *
st_search(
    struct suffix_tree *st,
    const char *pattern
);

I make an exception for functions that take no arguments, that is, have void as their
argument type.
There are many places where an algorithm needs to use characters to look up in
arrays. If you use the conventional C string type, char *, then the character can be either
signed or unsigned, depending on your compiler, and you have to cast the type to avoid
warnings. A couple of places we also have to make assumptions about the alphabet
size. Because of this, I use arrays of uint8_t with a zero termination sentinel as strings.
On practically all platforms, char is 8 bits so this type is, for all intents and purposes, C
strings. We are just guaranteed that we can use it unsigned and that the alphabet size

WOW! eBook
www.wowebook.org
Chapter 1 Introduction

is 256. Occasionally it is necessary to cast a uint8_t * string to a C string. A direct cast,


(char *)x, will most likely work unless you are on an exotic platform. If it doesn’t, you
have to build a char buffer and copy characters byte by byte. It has to be a very exotic
platform if you cannot store 8 bits in a char! Because I assume that you can always cast to
char *, I will use the C library string functions (with a cast) when this is appropriate. It is
a small matter to write your own if it is necessary.
I will use uint32_t for indices, assuming that strings are short enough that we
can index them with 32 bits. You can change it as needed, but I find it a good trade-­
off between likely lengths of strings and the space I need for data structures. I work in
bioinformatics, so hundreds of millions of characters are usually the longest I encounter.

Reporting a sequence of results


In search algorithms, we report each occurrence of a pattern. This sounds
straightforward, but there is a design choice in how we report the occurrences. Consider
the following algorithm. It is the Boyer-Moore-Horspool (BMH) algorithm that you
will see in the next chapter. It takes a string, x, and a pattern, p, and searches for all
occurrences of p in x. First, it does some preprocessing, and then it searches. This is a
general pattern for the algorithms in the next chapter. In the search, when it has found
an occurrence of p, it reports the position by calling the REPORT(j) function.

void bmh_search(
    const uint8_t *x,
    const uint8_t *p
) {
    uint32_t n = strlen((char *)x);
    uint32_t m = strlen((char *)p);

    // Preprocessing
    int jump_table[256];

    for (int k = 0; k < 256; k++) {


        jump_table[k] = m;
    }
    for (int k = 0; k < m - 1; k++) {
        jump_table[p[k]] = m - k - 1;
    }
5

WOW! eBook
www.wowebook.org
Chapter 1 Introduction

    // Searching
    for (uint32_t j = 0;
         j < n - m + 1;
         j += jump_table[x[j + m - 1]]) {

        int i = m - 1;
        while (i > 0 && p[i] == x[j + i])
            --i;
        if (i == 0 && p[0] == x[j]) {
            REPORT(j);
        }
    }
}

If a global report function is all you need in your program, then this is an excellent
solution. Often, however, we need different reporting functions for separate calls to the
search function. Or we need the report function to collect data for further processing
(and preferably not use global variables). We need some handle to choose different
report functions and to provide them with data.
One approach is using callbacks: Provide a report function and data argument to the
search function and call the report function with the data when we find an occurrence.
In the following implementation, I am assuming we have defined the function type for
reporting, report_function, and the type for data we can add to it, report_function_
data, somewhere outside of the search function.

void bmh_search_callback(
    const uint8_t *x,
    const uint8_t *p,
    report_function report,
    report_function_data data
) {
    uint32_t n = strlen((char *)x);
    uint32_t = strlen((char *)p);

    // Preprocessing
    uint32_t jump_table[256];

WOW! eBook
www.wowebook.org
Chapter 1 Introduction

    for (int k = 0; k < 256; k++) {


        jump_table[k] = m;
    }
    for (int k = 0; k < m - 1; k++) {
        jump_table[p[k]] = m - k - 1;
    }

    // Searching
    for (uint32_t j = 0;
         j < n - m + 1;
         j += jump_table[x[j + m - 1]]) {

        int i = m - 1;
        while (i > 0 && p[i] == x[j + i])
            --i;
        if (i == 0 && p[0] == x[j]) {
            report(j, data);
        }
    }
}

Callback functions have their uses, especially to handle events in interactive


programs, but also some substantial drawbacks. To use them, you have to split the
control flow of your program into different functions which hurts readability. Especially
if you need to handle nested loops, for example, iterate over all nodes in a tree and for
each node iterate over the leaves in another tree where for each node-leaf pair you find
occurrences… (the example here is made up, but there are plenty of real algorithms with
nested loops, and we will see some later in the book).
We can get the control flow back to the calling function using the iterator design
pattern. We define an iterator structure that holds information about the loop state,
and we provide functions for setting it up, progressing to the next point in the loop, and
reporting a match and then a function for freeing resources once the iterator is done.
The general pattern for using an iterator looks like this:

    struct iterator iter;


    struct match match;
    iter_init(&iter, data);

WOW! eBook
www.wowebook.org
Chapter 1 Introduction

    while (next_func(&iter, &match)) {


        // Process occurrence
    }
    iter_dealloc(&iter);

The iterator structure contains the loop information. That means it must save the
preprocessing data from when we create it and information about how to resume the
loop after each time it is suspended. To report occurrences, it takes a “match” structure
through which it can inform the caller about where matches occur. The iterator is
initialized with data that determines what it should loop over. The loop is handled
using a “next” function that returns true if there is another match (and if it does it will
have filled out match). If there are no more matches, and the loop terminates, then it
returns false. The iterator might contain allocated resources, so there should always be a
function for freeing those.
In an iterator for the BMH, we would keep the string, pattern, and table we build in
the preprocessing.

struct bmh_match_iter {
    const uint8_t *x; uint32_t n;
    const uint8_t *p; uint32_t m;
    int jump_table[256];
    uint32_t j;
};
struct match {
    uint32_t pos;
};

We put the preprocessing in the iterator initialization function

void init_bmh_match_iter(
    struct bmh_match_iter *iter,
    const uint8_t *x, uint32_t n,
    const uint8_t *p, uint32_t m
) {
    // Preprocessing
    iter->j = 0;
    iter->x = x; iter->n = n;
    iter->p = p; iter->m = m;
8

WOW! eBook
www.wowebook.org
Chapter 1 Introduction

    for (int k = 0; k < 256; k++) {


        iter->jump_table[k] = m;
    }
    for (int k = 0; k < m - 1; k++) {
        iter->jump_table[p[k]] = m - k - 1;
    }
}

and in the next function we do the search

bool next_bmh_match(
    struct bmh_match_iter *iter,
    struct match *match
) {
    const uint8_t *x = iter->x;
    const uint8_t *p = iter->p;
    uint32_t n = iter->n;
    uint32_t m = iter->m;
    int *jump_table = iter->jump_table;

    // Searching
    for (uint32_t j = iter->j;
         j < n - m + 1;
         j += jump_table[x[j + m - 1]]) {

        int i = m - 1;
        while (i > 0 && p[i] == x[j + i]) {
            i--;
        }
        if (i == 0 && p[0] == x[j]) {
            match->pos = j;
            iter->j = j +
                   jump_table[x[j + m - 1]];
            return true;
        }
    }
    return false;
}
9

WOW! eBook
www.wowebook.org
Chapter 1 Introduction

We set up the loop with information from the iterator and search from there. If we
find an occurrence, we store the new loop information in the iterator and the match
information in the match structure and return true. If we reach the end of the loop, we
report false.
We have not allocated any resources when we initialized the iterator, so we do not
need to free anything.

void dealloc_bmh_match_iter(
    struct bmh_match_iter *iter
) {
    // Nothing to do here
}

Since the deallocation function doesn’t do anything, we could leave it out. Still,
consistency in the use of iterators helps avoid problems. Plus, should we at some point
add resources to the iterator, then it is easier to update one function than change all the
places in the code that should now call a deallocation function.
Iterators complicate the implementation of algorithms, especially if they are
recursive and the iterator needs to keep track of a stack. Still, they greatly simplify the
user interface to your algorithms, which makes it worthwhile to spend a little extra time
implementing them. In this book, I will use iterators throughout.

10

WOW! eBook
www.wowebook.org
CHAPTER 2

Classical algorithms
for exact search
We kick the book off by looking at classical algorithms for exact search, that is, finding
positions in a string where a pattern string matches precisely. This problem is so
fundamental that it received much attention in the very early days of computing, and by
now, there are tens if not hundreds of approaches. In this chapter, we see a few classics.
Recall that we use iterators whenever we have an algorithm that loops over results
that should be reported. All iterators must be initialized, and the resources they hold must
be deallocated when we no longer need the iterator. When we loop, we have a function
that returns true when there is something to report and false when the loop is done. The
values the iterator reports are put in a structure that we pass along to the function that
iterates to the next value to report. For the algorithms in this chapter, we initialize the
iterators with the string in which we search, the pattern we search for, and the lengths of
the two strings. Iterating over all occurrences of the pattern follows this structure:

struct iterator iter;


struct match match;
iter_init(iter, x, strlen(x), p, strlen(p));
while (next_func(&iter, &match)) {
   // Process occurrence
}
iter_dealloc(&iter);

When we report an occurrence, we get the position of the match, so the structure the
iterator use for reporting is this:

struct match {
    uint32_t pos;
};

11
© Thomas Mailund 2020
T. Mailund, String Algorithms in C, https://doi.org/10.1007/978-1-4842-5920-7_2

WOW! eBook
www.wowebook.org
Chapter 2 Classical algorithms for exact search

N
 aïve algorithm
The simplest way imaginable for exact search is to iteratively move through the string
x, with an index j that conceptually runs the pattern p along x, and at each index start
matching the pattern against the string using another index, i (see Figure 2-1). The
algorithm has two loops, one that iterates j through x and one that iterates i through
p, matching x[i + j] against p[i] along the way. We run the inner loop until we see a
mismatch or until we reach the end of the pattern. In the former case, we move p one
step forward and try matching again. In the second case, we report an occurrence at
position j and then increment the index so we can start matching at the next position. We
stop the outer loop when index j is greater than n − m. If it is, there isn’t room for a match
that doesn’t run past the end of x.

Figure 2-1. Exact search with the naïve approach

We terminate the comparison of x[i + j] and p[i] when we see a mismatch, so in the best
case, where the first character in p never matches a character in x, the algorithm runs in
time O(n) where n is the length of x. In the worst case, we match all the way to the end of p
at each position, and in that case, the running time is O(nm) where m is the length of p.
To implement the algorithm using an iterator, the iterator needs to remember
the string to search in and the pattern to search for—so we do not need to pass these
along each time we increment the iterator with potentials for errors if we use the wrong
strings—and we keep track of how far into the string we have searched.

struct naive_match_iter {
    const uint8_t *x; uint32_t n;
    const uint8_t *p; uint32_t m;
    uint32_t current_index;
};

12

WOW! eBook
www.wowebook.org
Chapter 2 Classical algorithms for exact search

When we initialize the iterator, we remember the two strings and set the current
index to zero—before we start iterating we are at the beginning of the string.

void init_naive_match_iter(
    struct naive_match_iter *iter,
    const uint8_t *x, uint32_t n,
    const uint8_t *p, uint32_t m
) {
    iter->x = x; iter->n = n;
    iter->p = p; iter->m = m;
    iter->current_index = 0;
    iter->current_index = 0;
}

When we increment the iterator, we follow the algorithm as described earlier except
that we start the outer loop at the index saved in the iterator. We search from this index in
an outer loop, and at each new index, we try to match the pattern with an inner loop. We
break the inner loop if we see a mismatching character, and if the inner loop reaches the
end, we have a match and report it. Before we return, we set the iterator index and store
the matching position in the match structure.

bool next_naive_match(
    struct naive_match_iter *iter,
    struct match *match
) {
    uint32_t n = iter->n, m = iter->m;
    const uint8_t *x = iter->x;
    const uint8_t *p = iter->p;

    if (m > n) return false;


    if (m == 0) return false;

    for (uint32_t j = iter->current_index; j <= n - m; j++) {


        uint32_t i = 0;
        while (i < m && x[j+i] == p[i]) {
            i++;
        }

13

WOW! eBook
www.wowebook.org
Chapter 2 Classical algorithms for exact search

        if (i == m) {
            iter->current_index = j + 1;
            match->pos = j;
            return true;
        }
    }

    return false;
}

The code

    if (m > n)  return false;


    if (m == 0) return false;

makes sure that it is possible to match the pattern at all and that the pattern isn’t
empty. This is something we could also test when we initialize the iterator. However, we
do not have a way of reporting that we do not have a possible match there, so we put the
test in the “next” function.
We do not allocate any resources when we initialize the iterator, so we do not need to
do anything when deallocating it either. We still need the deallocator function, however,
so we always use the same design pattern when we use iterators. To make sure that if we,
at some point in the future, need to free something that we put in an iterator, then all
users of the iterator (should) have added code for this.

void dealloc_naive_match_iter(
    struct naive_match_iter *iter
) {
    // Nothing to do here...
}

Border array and border search


It is possible to get O(n + m) running times for both best and worst case, and several
algorithms exist for this. We will see several in the following sections. The first one is
based on the so-called borders of strings.

14

WOW! eBook
www.wowebook.org
Chapter 2 Classical algorithms for exact search

Borders and border arrays


A border of a string is any substring that is both a prefix and a suffix of the said string; see
Figure 2-2. For example, the string x = ababa has borders aba, a, and the empty string.
There is always at least one border per string—the empty string. It is possible to list all
borders by brute force. For each index i in x, test if the substrings x[0, i] matches the
string x[n − i, n]. This approach makes time O(n) per comparison, and we need it for all
possible borders which means that we end up with a running time of O(n2). It is possible
to compute the longest border in linear time, as we shall see. The way we compute it
shows that sometimes more is less; we will compute more than the length of the longest
suffix. What we will compute is the border array. This is an array that for each index i
holds the length of the longest border of string x[0, i]. Consider x = ababa. For index 0 we
have string a which has border a, so the first element in the border array is 1. The string
ab only has the trivial, nonempty border, so the border array value is zero. The next string
is aba with border a, so we get 1 again. Now abab has borders ab, so the border array
holds 2. The full string x = ababa with border aba so its border array looks like
ba = [1, 0, 1, 2, 3].

Figure 2-2. A string with three borders

We can make the following observation about borders and border arrays: The longest
border of x[0, i] is either the empty string or an extension of a border of x[0, i − 1]. If the
letter at x[i] is a, the border of x[0, i] is some string y followed by a. The y string must be
both at the beginning and end of x[0, i − 1] (see Figure 2-3), so it is a border of x[0, i − 1].
The longest border for x[0, i] is the longest border of x[0, i − 1] that is followed by a (which
may be the empty border if the string x begins with a) or the empty border if there is no
border we can extend with a.
Another observation is that if you have two borders to a string, then the shorter of the
two is a border of the longer; see Figure 2-4.

15

WOW! eBook
www.wowebook.org
Chapter 2 Classical algorithms for exact search

The two observations combined gives us an approach to computing the border


array. The first string has the empty border as its only border, and after that, we can use
the border array up to i − 1 to compute the length of the longest border of x[0, i]. We start
by testing if we can extend the longest border with x[i], and if so, ba[i] = ba[i − 1] + 1.
Otherwise, we look at the second-longest border, which must be the longest border of
x[0, ba[i − 1]]. If the character after this border is x[i], then ba[i] = ba[ba[i − 1]] + 1.
We continue this way until we have found a border we can extend (see Figure 2-5). If we
reach the empty border, we have a special case—either we can extend the empty border
because x[0] = x[i], in which case ba[i] = 1, or we cannot extend the border because
x[0] ≠ x[i], in which case ba[i] = 0.

Figure 2-3. Extending a border

Figure 2-4. A shorter border is always a border of a longer border

16

WOW! eBook
www.wowebook.org
Chapter 2 Classical algorithms for exact search

Figure 2-5. Searching for the longest border we can extend with letter a

An implementation of the border array construction algorithm can look like this:

    ba[0] = 0;
    for (uint32_t i = 1; i < m; ++i) {
        uint32_t b = ba[i - 1];
        while (b > 0 && x[i] != x[b])
            b = ba[b - 1];
        ba[i] = (x[i] == x[b]) ? b + 1 : 0;
    }

The running time is m for a string x of length m. It is straightforward to see that


the outer loop only runs m iterations but perhaps less easy to see that the inner loop
is bounded by m iterations in total. But observe that in the outer loop, we at most
increment b by one per iteration. We can assign b + 1 to ba[i] in the last statement in the
inner loop and then get that value in the first line of the next iteration, but at no other
point do we increment a value. In the inner loop, we always decrease b—when we get the
border of b − 1, we always get a smaller value than b. We don’t allow b to go below zero in
the inner loop, so the total number of iterations of that loop is bounded by how much the
outer loop increase b. That is at most one per iteration, so we can decrement b by at most
m, and therefore the total number of iterations of the inner loop is bounded by O(m).

17

WOW! eBook
www.wowebook.org
Chapter 2 Classical algorithms for exact search

Exact search using borders


The reason we built the border array was to do an exact search, so how does the array
help us? Imagine we build a string consisting of the pattern we search for, p, followed
by the string we search in, x, separated by a sentinel, $ character not found elsewhere in
the two strings, y = p$x. The sentinel ensures that all borders are less than the length of
p, m, and anywhere we have a border of length m, we must have an occurrence of p (see
Figure 2-6). In the figure, the indices are into the p$x string and not into x, but you can
remap this by subtracting m + 1. The start index of the match is i − m + 1 rather than the
more natural i − m because index i is at the end of the match and not one past it.
We can construct the string p$x in linear time and compute the border array—and
report occurrences in the process—in linear time, O(m + n). You don’t need to create
the concatenated string, though. You can build the border array for p and use that when
computing the border array for x. You pretend that p is prepended to x. When you do
this, the sentinel between p and x is the null-termination sentinel in the C-string p.

Figure 2-6. Searching using a border array

An iterator that searches a string with this algorithm must contain the border
array of p, the index into x we have reached, and the b variable from the border array
construction algorithm.

struct border_match_iter {
    const uint8_t *x; uint32_t n;
    const uint8_t *p; uint32_t m;
    uint32_t *border_array;
    uint32_t i; uint32_t b;
};

18

WOW! eBook
www.wowebook.org
Chapter 2 Classical algorithms for exact search

When we initialize the iterator, we set its index to zero. That, after all, is where we
start searching in x. We also set the iterator’s b variable to zero. We imagine that we start
the search after a sentinel, so the longest border at the start index for x has length zero.
We then allocate and compute the border array.

void init_border_match_iter(
    struct border_match_iter *iter,
    const uint8_t *x, uint32_t n,
    const uint8_t *p, uint32_t m
) {
    iter->x = x; iter->n = n;
    iter->p = p; iter->m = m;
    iter->i = iter->b = 0;

    uint32_t *ba = malloc(m * sizeof(uint32_t));


    compute_border_array(p, m, ba);
    iter->border_array = ba;
}

Since we allocated the border array when we initialized the iterator, we need to free it
again when we deallocate it.

void dealloc_border_match_iter(
    struct border_match_iter *iter
) {
    free(iter->border_array);
}

A third of my implementation for incrementing the following iterator is setting up


aliases for the variables in the iterator, so I don’t have to write iter->b and iter->m
for variables b and m, respectively. Other than that, there are the tests for whether it is
possible at all to have a match, that we also saw in the previous section, and then there is
the border array construction algorithm again, except that we never update an array but
instead report when we get a border of length m.

19

WOW! eBook
www.wowebook.org
Chapter 2 Classical algorithms for exact search

bool next_border_match(
    struct border_match_iter *iter,
    struct match *match
) {
    const uint8_t *x = iter->x;
    const uint8_t *p = iter->p;
    uint32_t *ba = iter->border_array;
    uint32_t b = iter->b;
    uint32_t m = iter->m;
    uint32_t n = iter->n;

    if (m > n) return false;


    if (m == 0) return false;

    for (uint32_t i = iter->i; i < iter->n; ++i) {


        while (b > 0 && x[i] != p[b])
            b = ba[b - 1];
        b = (x[i] == p[b]) ? b + 1 : 0;
        if (b == m) {
            iter->i = i + 1;
            iter->b = b;
            match->pos = i - m + 1;
            return true;
        }
    }

    return false;
}

When we report an occurrence, we naturally set the position we matched in the


report structure, and we remember the border and index positions.

20

WOW! eBook
www.wowebook.org
Chapter 2 Classical algorithms for exact search

K
 nuth-Morris-Pratt
The Knuth-Morris-Pratt (KMP) algorithm also uses borders to achieve a best- and worst-­
case running time of O(n + m), but it uses the borders in a slightly different way. Before
we get to the algorithm, however, I want to convince you that we can, conceptually, move
the pattern p through x in two different ways; see Figure 2-7. We can let j be an index
into x and imagine p starting there. When we test if p matches there, we use a pointer
into p, i, and test x[ j + i] against p[i] for increasing i. To move p to another position in x,
we change j, for example, to slide p one position to the right we increment j by one.
Alternatively, we can imagine p aligned at position j − i for some index j in x and an
index i into p. If we change i, we move j − i so we move p. If, for example, we want
to move p one step to the right, we can decrement i by one. To understand how the
KMP algorithm works, it is useful to think about moving p in the second way. We will
increment the j and i indices when matching characters, but when we get a mismatch,
we move p by decrementing i.
The idea in KMP is to move p along x as we would in the naïve algorithm, but move
a little faster when we have a mismatch. We use index j to point into x and i to point
into p. We match x[ j] against p[i] as we scan along x and the pattern is aligned against
x at index j − i. We can move p’s position by modifying either i or j. Consider a place
in the algorithm where we have matched p[0, i] against x[ j − i, j] and see a mismatch.
In the naïve algorithm, we would move p one step to the right and start matching p
again at that position. We would set i to zero to start matching from the beginning of p,
and we would decrement j to j − i + 1 to match at the new position at which we would
match p. With KMP we will skip positions where we know that p cannot match, and we
use borders to do this.

21

WOW! eBook
www.wowebook.org
Discovering Diverse Content Through
Random Scribd Documents
The Project Gutenberg eBook of Suomen kansan
eläinkirja
This ebook is for the use of anyone anywhere in the United States
and most other parts of the world at no cost and with almost no
restrictions whatsoever. You may copy it, give it away or re-use it
under the terms of the Project Gutenberg License included with this
ebook or online at www.gutenberg.org. If you are not located in the
United States, you will have to check the laws of the country where
you are located before using this eBook.

Title: Suomen kansan eläinkirja


Kertomus Metsolan ja Ilmolan väestä ja elämästä

Author: Eino Railo

Release date: February 12, 2024 [eBook #72937]

Language: Finnish

Original publication: Helsinki: Kust.Oy Kirja, 1921

Credits: Juhani Kärkkäinen and Tapio Riikonen

*** START OF THE PROJECT GUTENBERG EBOOK SUOMEN


KANSAN ELÄINKIRJA ***
SUOMEN KANSAN ELÄINKIRJA

Kertomus Metsolan ja Ilmolan väestä ja elämästä

Kansansaduista sommitellut

Osmo Iisalo [Eino Railo]

Suomalaisen Kirjallisuuden Seuran toimituksia 167 osa

Helsingissä, Kustannusosakeyhtiö Kirja, 1921.

SISÄLLYS:
»Suomen kansan eläinkirja», alkulause.
I. Eläimet tekevät tiet Metsolaan; kettu työpehtorina.
II. Ahkerat palkitaan; karhu ja muurahainen; pyyn tarina;
ketun palkka.
III. Laiskoja rangaistaan; päästäjäinen kirotaan;
käärmeen tarina.
IV. Samoin; palokärjen tarina, haukka ja hiiriäistuuppa;
käen tarina.
V. Samoin kyyhkynen ja sammakko, kyyhkynen ja metsäkana;
tiainen
rangaistaan; rastaan tarina; pääskysen tarina; yökön tarina.
VI. Karhun ja suden ahneus sekä Tapion rangaistus. — Koira
menee ihmisen luo.
VII. Kettu tutkii loukkaan ja houkuttelee siihen karhun, jonka
hiiri pelastaa.
VIII. Koira pelastaa Pohjolalle elämisen mahdollisuuden. — Mies
rakentaa pyydyksensä eläinten teille; hänen ja eläinten
sopimus molemminpuolisista
oikeuksista. — Karhun ja suden synty.
IX. Kettu houkuttelee suden kuoppaan. — Kettu ja teeri.
X. Variksen vala; sammakon tarina.
XI. Kettu ja varsa; korpin juusto; harakka ja suksipuun hakija;
kettu ja varis.
XII. Kettu ja jänis.
XIII. Kettu ja kurki.
XIV. Karhu ja jalopeura sekä heidän sotansa; mies vaimonpoika.
XV. Karhun eukon kuolema; itkijän ja lapsenpiian haku; kettu
itkijänä; tuulensuunnat; meden haku.
XVI. Suden kirjavoiminen; kolmen puun nimi; tamman alahuuli;
susi oppii lentämään.
XVII. Kettu ja suden akka; kettu pakenee muille maille; hänen
muinainen kataluutensa.
XVIII. Lähtö Ilmolaan pahantekoon; avulias akka palkitaan; kolme
oinasta; Horpon ruuna ja karhu; sormikoukun veto ja muut
karhun onnettomuudet; jänis ja paarma auttavat karhua.
XIX. Horpon isännän ja karhun yhteinen kaski. — Metson
isänmaanrakkaus. — Karhu rovastina.
XX. Piippolan sika ja päivän nousu; sian lersa kärsä. —
Immilän ylpeä kukko.
XXI. Immilän kukon esi-isän kuninkaissa käynti.
XXII. Kettu ja Immilän kukko; eläinten rauha; kukon ja kissan
talous.
XXIII. Kettu ja Piippolan vaarin kalanpyynti; karhu onkii hännällään
kaloja; kettu syö kirnuuksen; »Sairas tervettä kantaa»;
karhun kosto; kettu syö uudelleen viiliä.
XXIV. Susi Kääpälään; Merkin vanhuuden onnettomuudet; susi
auttaa Merkkiä; Merkki kertoo penikalle tarinan susista ja
kadonneesta pöytäkirjasta.
XXV. Susi, pukki ja pässi; susi ja räätäli; porsasten kastaminen;
pukin maan vakoaminen; pässien riidan ratkaiseminen;
lampaille veisaaminen; tamman passin katsominen.
XXVI. Merkki juottaa suden humalaan.
XXVII. Jänis ja pakkanen. — Kissan ja hiiren synty. — Jäniksen ja
kissan yhteinen talous. — Jäniksen pelastuskeinot. — Kissa
ja päästäjäinen. — Kissan vilja ja jäniksen vilja. — Kissan
ja koiran metsästys. — Jänis jättää kissan. — Kissan osuus
lehmän antiin.
XXVIII. Hiiren ja hänen sukunsa historia; hiiri ja sammakko; hiiri
räätälinä; katti kosiomiehenä; kello kissan kaulaan.
XXIX. Pukki ja Kääpälän vaari; pukki Katilan autiotuvassa ja hänen
karkoittajansa: karhu, hukka, peura ja kukko. — Jänis
takaisin Metsolaan.
XXXX. Horpon isäntä juottaa karhun humalaan. — Karhu
loukkaassa.
— »Maksan niinkuin maailma maksaa.» — Jänis, hevonen,
koira
ja kettu tuomareina. — Ketun palkka.
XXXI. Karhun, suden ja ketun kaskenkaato; Piippolan muorin
voipytty;
ketun ristiäisissä käynti; kettu karkotetaan.
XXXII. Kääpälän kissa ketulle rengiksi; kissan topakka suku.
XXXIII. Kettu kertoo karhulle ja sudelle uudesta rengistänsä; sen
kärpäset, nimet ja aseet; ja susi kärpäsiä katsomassa; karhu
ja susi herra Vinkkeliä katsomassa.
XXXIV. Karhu ja susi laittavat pidot herra Vinkkelille ja ketulle.
XXXV. Karhu ja susi hakevat herra Vinkkeliä ontosta hongasta.
XXXVI. Metsän eläimet ryhtyvät sotimaan kotieläimiä vastaan.
XXXVII. Kääpälän vaari joutuu kärpän saaliiksi; yön vietto.
XXXVIII. Yötä viettäessä tarinoidaan: Orava, neula ja kinnas.
XXXIX. Lisää tarinoita: Jalopeura, hevonen, susi ja karhu; kettu
houkuttelee karhun syömään suoliansa.
XL. Kevät; rastas ja Vänskän Aatami; metsäkana; Kirppu-Jussi
ja korppi.
XLI. Lintujen käräjät; pajulintu ja köyhän miehen kylvös;
pääskysen silkinvarkaus.
XLII. Pääskynen ja varpunen; varpunen ja raunioruntti; raunioruntti
ja kivenviha.
XLIII. Raunioruntti ja västäräkki; västäräkki, rastas ja koskikara;
västäräkki ja rantasipi.
XLIV. Tiltaltti, rastas, kyyhkynen ja varis; tiainen ja Musti;
käki ja närhi; rastas ja koppelo.
XLV. Karhu, susi ja kettu polttavat kaskensa.
XLVI. Immilän kukon oluenjuonti ja humala; eläinten retki
maailmanloppua pakoon.
XLVII. Ilmolan pelimanni opettaa karhua soittamaan.
XLVIII. Kiisken kirkko; kalojen käräjät Airistolla; kiiski kalojen
kuningas.
XLIX. Kerjäläisakka, karhu, susi, repo ja jänis.
L. Koiraseläimet ja Kääpälän kissa pakenevat Immilästä.
LI. Immilän rikkauden alku: mustan härän tarina.
LII. Outo vieras autiotuvalla ja sen karkoitus.
LIII. Karhu, susi ja kettu leikkuupellolla, riihessä ja myllyllä;
karhu ja susi maistavat ketun puuroa; karhun puuronkeitto;
suden kuolema.
LIV. Karhu tahtoo syödä Horpon isännän hevosen; ketun neuvot;
»Karhujen tappaja ja miesten sitoja»; karhun kuolema.
LV. Ketun palkka ja kuolema.
LVI. Tiet tehty Suomen ympäri, Metsolan eläimet karkoitettu.

Alkulause.

»Suomen kansan eläinkirja», joksi olen uskaltanut tämän


suomalaisen eläinromaanin kokeen nimittää, on syntynyt
eläinsatujemme pohjalla. Lähteinä ovat olleet pääasiallisesti
professori Kaarle Krohnin julkaisemat eläinsadut [Suomalaisia
kansansatuja, I osa, eläinsatuja. 1886. Suomalaisen Kirjallisuuden
Seuran toimituksia, 67 osa]; lisäksi olen tarkastanut joukon
julkaisemattomia toisintoja, saamatta niistä kuitenkaan
sanottavampia lisiä.

Eläinsatuja lukiessani on mielessäni aina herännyt ajatus, että


niissä piilee enemmän kuin paljas tarkoitus huvittaa lapsia sukkelilla
jutuilla. Ensinnäkin ne ovat ikäänkuin jäljelle jääneitä tahi näkyvissä
olevia sirpaleita jostakin uponneesta, kerran eheästä
kokonaisuudesta, joka olisi yhtenäisesti kertonut eläimistä ja ihmisen
suhteesta niihin. Toiseksi ne ovat, kuvatessaan tämän suhteen
vaiheita, joutuneet kertomaan siitä, kuinka ihminen vähitellen voittaa
metsän eläimet, karkoittaen heidät yhä kauemmaksi viljelyksen tieltä.
Kolmanneksi on kansamme runollinen mielikuvitus nähdäkseni
saavuttanut niissä erään kauneimmista voitoistaan luodessaan
onnistuneen, humoristisen luonnekuvan useista metsän ja kodin
eläimistä. Neljänneksi se on niihin sisällyttänyt paljon tavallisessa
elämässään saavuttamaansa luonnonkäsitystä, siveellisiä
periaatteitansa ja omaa kulttuurihistoriaansa, vaistomaisesti asettaen
siinä oman kokemuksensa mukaisesti ikäänkuin kaiken keskustaksi
ja huipuksi maanviljelyksen, karhu paran suurenmoisen
kaskenkaadon. Kokonaisuutena siis eläinsatumme muodostavat
mielestäni rikassisältöisen viljelyksen eepoksen, jossa veikeän ja
humoristisen puvun alla on vakava ja ankara tosiasia.

Sadut lienevät enemmän kuin muu kansanrunous aiheiltaan


kansojen yhteistä omaisuutta. Eläinsatuihinsa on meidän kansamme
kuitenkin mielestäni painanut niin paljon oman luonteensa,
luonnonkäsityksensä ja kokemuksiensa leimaa, että niiden pohjalla
syntynyt eläinromaani on erikoisesti suomalainen, enimmäkseen
toista kuin esim. kuvaannolliset Reinecke-historiat. Nämä
jälkimmäiset ovat pääosaltaan moralisovia kettujuttuja, vailla
korkeampaa runollista perusaatetta, joka taas, kuten ainakin
allekirjoittanut on asian käsittänyt, muodostaa meidän
eläinsaduissamme huomattavalla kauneustasolla olevan pohjan;
tämän antamassa valossa on sitten nähty kaikki muu, ylinnä kettu
juoninensa.

Kertomuksen juoneksi olen asettanut metsän ja kodin eläinten


sekä ihmisen, s.o. Metsolan ja Ilmolan vastakohdan, niinkuin se
itsestäänkin saduista ilmenee; tämän rinnalla, välittäjänä ja viejänä
tapauksesta toiseen, toiminnan antajana, esiintyy pääasiassa kettu.
Tähän lankaan olen sitten koettanut ripustaa näkyville kaikki
ainekset, niin paljon kuin suinkin olen niitä saanut kokonaisuuteen
mahtumaan. Ainekset olen aluksi luokitellut aiheitten mukaan
käyttäen siinä apuna professori Antti Aarnen tyyppiluetteloita
[Verzeichnis der Märchentypen. F F Communications n:o 3, 1910;
Finnische Märchenvarianten. F F Communications n:o 5, 1911;
Sama, Ergänzungsheft 1, F F Communications n:o 33, 1920]; nämä
eri toisinnot olen sulattanut yhteen yhtenäisiksi tapaussarjoiksi ja
koettanut samalla puoleksi ulkoa lukien tehdä omakseni niiden
kansanomaisen kielen ja kertomatavan. Näin menetellen luulen
yleensä onnistuneeni välttämään suurempaa väkivaltaa ja »omia»
lisäyksiä.

Mitä kertomatyyliin muuten tulee, olen katsonut siinä oikeudekseni


kykyni mukaan pyrkiä mahdollisen suureen mehevyyteen, jopa
kansanomaiseen leveyteenkin. Käsitykseni on nimittäin se, että
kansamme kertoo — ainakin itäsuomalaisella alueella — omissa
oloissaan, vain tuttua väkeä kuulijana, satunsa elävästi, mehevästi ja
laajasti; tästä on itselläni varma muisto lapsuuteni ajalta
Suomussalmelta, jossa kolmisenkymmentä vuotta sitten
sadunkertominen vielä oli pirttien suosittua iltahuvia. Mutta heti kun
kirjaanpanija, satujen kerääjä, joka useinkin on ylioppilas tahi muu
»herra», saapuu kyninensä ja papereinensa kertojan ääreen, valtaa
tämän luonnollisesti ujous, ja koska hän muutenkin pitää satuja
»loruina», tulee hän väkisinkin muuttaneeksi kertomatapaansa.
Niinpä ovatkin usein kirjaanpannut toisinnot yksittäin luettuina
sangen »kuivia», jopa vähäpätöisiäkin, sisältäen kuitenkin tavallisesti
sen ytimen, joka sopivasti paisutettuna antaa sille tarpeellisen lihan
ja veren.

Teokseni synnystä on minun kiittäminen Suomalaisen


Kirjallisuuden Seuraa, jonka arvoisa johtokunta salli minun sitä
yrittää ja joka on kunnioittanut työni tulosta ottamalla sen
»Toimituksiinsa»; professori Kaarle Krohnia, joka on pitkin matkaa
avustanut minua arvokkailla neuvoilla, m.m. kohdistamalla huomioni
Metsolan ja Ilmolan vastakohtaisuuteen ja lukemalla
käsikirjoitukseni, jolloin hänen neuvoistaan tulin ymmärtämään
monen lyhennyksen ja korjauksen tarpeellisuuden; maisteri E.A.
Saarimaata, joka samoin on lukenut käsikirjoitukseni ja antanut
useita arvokkaita neuvoja sekä tehnyt kielellisiä silityksiä; tohtori E.A.
Tunkeloa, joka hyväntahtoisesti on lukenut korjausarkit ja saanut
minut kiitollisena korjaamaan monet kielelliset ja tyylilliset
epätarkkuudet; tohtori Santeri Ivaloa ja tohtori Juhani Aho vainajaa,
jotka suopealla ja luottavalla suhtautumisellaan yritykseeni ovat
minua paljon siihen innostuttaneet. Näille arvoisille henkilöille lausun
parhaimmat kiitokseni, valittaen, ettei Juhani Ahon oltu sallittu olla
teokseni kummina niin pitkälle kuin hän itse asiaan mieltyneenä, sen
alkuvaiheiden aikana, sanoi haluavansa.

Pälkäneellä, elok. 27 p. 1921.

Osmo Iisalo.
I.

TIE KÄYDÄ, HAKO LEVÄTÄ.

Susi juoksi suota myöten,


Karhu kangasta samosi.
Suo liikkui suden jälessä,
Kangas karhun kämmenissä.

Olipa kerran, kauan aikaa sitten — siitä on jo niin pitkiä aikoja,


ettei sitä voi kukaan sanoa — Suomenniemellä niin soma elämä,
että eläimet osasivat puhua, haastella, tarinoida, jopa oikein selvällä
ja ymmärrettävällä kielellä asioitansa toisilleen ilmoittaa. Silloin olivat
täällä salot koskemattomat, järvet pyydyksen heittämättömät, kaikki
luonto ihanana ja kauniina Tapion ja Mielikin häiritsemättömässä
rauhassa. Missä tuli nälkä, siinä sen luonto kohta tyydytti, missä
jano, siinä heti juotavaksi kirkas lähde kumpusi, missä uni, siinä
pehmeä sammal nukuttavaksi. Eläimet elivät sopuisasti metsän
lakien mukaan, kukin luontonsa jälkeen, tyytyväisinä Pohjolan
kultaisen kesän ja valkoisen talven raikkaassa hoivassa.

Väliin hekin kuitenkin tuskittelivat, kun piti saalista etsiessä juosta


murrokoissa ja risukoissa, rikkoa jalkansa karkeissa kivikoissa,
sokaista silmänsä sakeissa tureikoissa. »Kunpa olisi täällä hyvät tiet
kaikkialla, joita myöten juoksennella, niin eri helppoa olisi meillä
elämä», arvelivat he ja neuvottelivat asiasta keskenään. Niinpä siitä
vihdoin herää ajatus ja kypsyy päätös: »Täytynee ruveta tässä teitä
tekemään ja risukkoja raivaamaan, viidakoita aukomaan, että on
hyvä mennä vilahduttaa sileätä polkua pitkin saalismaille.» Ei muuta
sitten kuin ruvetaan sanaa asiasta toisilleen saattamaan. Karhu
mörisee sitä kankailla kulkiessaan ja marjoja makuisasti
syödessään, susi ulvoo sen ilmoille istuessaan pesänsä edustalla,
korppi kronksahuttaa sen lentäessään, harakka nauraa räksähyttää
sen kuuluville pyrstöään heilauttaessaan, kärppä vie sen vilauksessa
hiirille ja kaikille sammalen asukkaille — pian on se Metsolan
asukkailla tietona ja kohta kaikki hokemaan: »Ka, tien tekoon on nyt
tästä lähdettävä, tien tekoon jotta oikein», ja pienet hiiretkin uhkaavat
ylpeästi: »Tien tekoon täytyy nyt ryhtyä, muu ei auta!»

Alkaa siinä sitten vilske metsässä, kun elävät kaikki kokoontuvat


Metsolan suurelle kankaalle päättämään yhteisestä tien teosta.
Tulee vyöryen karhu karvaröllö, tulee susi korentoselkä, kettu
neulasilmä, tulee kurki pillisääri, kärppä vikeväjalka ja hiiri
siimahäntä. On siinä ääntä ja jaaritusta, kun koko Metsolan väki
pitää neuvoa tien teosta, mistä alotetaan ja kuinka työ sitten
tehdään. Puhutaan, puhutaan — ei tule selvää miten on asiaan
käsiksi käytävä. Sanotaan: »Päällikkö tässä tarvitaan — kuka nyt
mieluisimmasti siksi rupeaa ja luulee osaavansa päällikkönä olla?»

Katsotaan toisiinsa töllötetään, karhu vilpittömästi katsoa illistelee,


susi tyhmästi, mutta kettu viekkaasti silmiänsä raottelee vasten
päivää. Kun ei kukaan sano mitään, vaan kaikki odottavat, niin jo
hyppää kettu neuvoskivelle, leuhauttaa tuuheata häntäänsä ja
julistaa: »Minä olen Mikko, mielevä mies, kyllä minä osaan olla
päällikkönä.»

No, tuosta ei kovin hämmästytä, sillä huomannut on jo Metsolan


väki ketun viisaaksi ja mieleväksi mieheksi ja nähnyt hänen
neuvokkuutensa monessa paikassa. Urahdetaan vain, että no,
ruvetkoon sitten kettu päälliköksi, vaikka älytäänkin, että laiskuuttaan
se siihen tarjousi, kun ei, liuvari, koskaan mitään työtä viitsisi tehdä.
Mutta ei siitä nyt ruveta riitelemään, sovinnollisia kun ollaan, vaan
alistutaan Mikon johtoon. Tämä rupeaakin topakasti töitä
järjestelemään, kääntyy karhun puoleen ja sanoo:

»Sinä karhu, kuomaseni, joka ylen väkevä mies olet, sinun pitää
vääntää pois kannot ja kivet…»

Karhu mörisi, että raskaimman työn sinä nyt hänelle annat, mutta
kettu siihen ehätti liukkaasti sanomaan:

»No, susi, kuomaseni, sinä toinen vankka mies, korentoselkä ja


vonkulasääri, sinä kantelet pois kannot ja kivet, joita kouko irti
vääntelee. Ja missä ei sitä työtä riitä, vaan maat ovat sileät, siinä
sinä maata kuovit, viskot pois lahopökkelöitä ja liekoja pois
kanniskelet…»

Nousevat siitä karhu ja susi sekä lähtevät työmaalleen, mutta kettu


sievästi edelleen toimittaa:

»Te, hiiret, sammalta pois tieltä repikää ja lykätkää; ja sinä, kärppä


sihtimuoto, valvo hiirien töitä. Jos käyvät työssä vaivaisiksi ja
kuolevat, niin ne kanna pois, ja elleivät tee työtä, niin saat syödä niitä
sen kuin jaksat.»
Ja topakasti kettu edelleen julistaa:

»Sinä ilves, metsänkiivas, saat repiä teiltä kanervia ja juuria,


sinulla on hyvät kynnet pehertää ja pehmittää tien paikkaa. Ja sinä,
jänis, mulkosilmä, jolla on niin villavat takajalat, sinä saat sitten sen
tasoitella, mitä ilves kuopii. Mutta orava pöyryhäntä ja silvankaristaja
hypätköön edellä tien suuntaa näyttämässä…»

Siitä toiset sanomaan, että kovin tulee mutkainen tie sillä tavalla,
mutta kettu määräsi: »Ei se haittaa mitään, olkoon vain mutkia
metsän teissä.»

Mutta linnuille kettu selitti:

»Teidän pitää syödä marjat pois tieltä ja repiä puolukan lehtiä ja


marjanvarsia.»

Sanoa keikahutti siihen silloin kurki, pitkäkaula:

»En minä viitsi tietä tehdä. Minä asun suolla enkä kankaalla.»

Mutta kettu kovasti varoitti kurkea tottelemaan ja pakotti hänet


työhön. Metsäkanan harakopran hän pani katkomaan latvoja ja
syömään urpuja niistä paikoista, mistä orava menee, että nähdään
tien paikka. Metsäkana, joka syntisyydessään oli käynyt Ristuksen
haudalla kirota vätkyttämässä, ei saattanut nytkään pahaa sisuansa
hillitä, vaan lähti menemään sadatellen, että »se tuhat tulinen p—
rkele sinulle rupesi tietä paaraamaan.» Teeri, se kiveräpyrstö
kukertaja, sai tehtäväkseen syödä tien paikalta koivun urvut, mutta
pyylle kukkapäälle, metsän synkän piiskuttajalle, annettiin lepän
urvut. Metso, värisilmä ja kupumaha, otti syödäkseen tiellä olevista
petäjistä havuneulasia merkiksi, pannen vielä akkansa, koppelon,
samaan puuhaan. Orava kerskasi: »Kukas muu kuuseen kuvahtaa,
ellei orava osaa», meni kuusikkoon, karsi käpyjä, söi siemeniä ja
pudotteli kävyn suomuja maahan. Mutta kuusanko, Tapion lintu,
hienorinta metsänpiilo, ja käpyhakkari puunkopistaja, ottivat
männynkävyt osalleen, uhaten niitä koota ja niillä rikastua.

Niin oli vihdoin saatu työt kullekin määrätyksi, mutta monet


Metsolan eläimet olivat jääneet työhön tulematta. Toiset kuitenkin
tekivät työtä sitä uutterammasti, karhu vääntäen kantoja ja kiviä
yhdeksän miehen voimalla, susi hänen jälkiänsä siivoten, hiiret
kovasti ponnistellen ja väsyen sekä joutuen voutinsa, kärpän
vikeväjalan pois kannettaviksi. Kettu liuvari vain käveli ylimpänä
työpehtorina milloin missäkin, söi ja huvitteli sekä jakoi mahtavasti
määräyksiään, paistatte!! päivää aurinkoisella rinteellä, ja nauroi
itsemielessään muitten eläimien tyhmyydellä. Mutta siitä, että toiset
jäivät työstä pois, toisten raskaasti raataessa, kylväytyi Metsolan
eläväin keskuuteen ensimmäinen kateuden ja eripuraisuuden
siemen, josta heille vastaisuudessa koitui paljon vahinkoa ja
turmiota. He tekivät kuitenkin ahkerasti työtänsä iltaan saakka, jolloin
Pohjolan kesäyön pehmeä hämärä laskeutui ja he kaikki riensivät
yöteloillensa tahi luontonsa mukaan hankkimaan saalistansa.
Metsolan suuri salo muuttui salaperäiseksi, lumoavaa loihtua täynnä
olevaksi maailmaksi, jossa metsän väki ja ilman ihmeet juoksivat ja
lensivät omilla äänettömillä teillänsä, yötuulen suhahdellessa korven
kuusikossa ja huuhkajan harmaakaulan kaameasti huhuillessa.
II.

AHKERUUS ON ONNEN ÄITI.

Tuosta tunnen virkun miehen,


Pian söi, välehen kenki,
Pian pistihen pihalle,
Terävähän töille lähti.

No, saadaan siitä vihdoin Metsolaan eläväin tiet valmiiksi. Kaikki


on nyt kunnossa. Tietää nyt kontio, mistä sen tulee samota, tietää
ilves metsäpolkunsa, orava osoittimensa, kärppä kaikki käymäpaikat,
tietävät teeret tien urpukoivuihinsa, metso männikköihinsä ja
peherryspaikoillensa, hirvet taistotanterillensa — kaikki on nyt
selväksi urat rastittu jokaiselle. Ei muuta kuin vielä kerta
kokoonnutaan suurelle Metsolan tanhualle tien teon päättäjäisiin.
Kettu siinä taas esiintyy puuhamiehenä kuin ylimmäinen työpehtori
ainakin ja julistaa kokoontuneelle Metsolan rahvaalle:

»Nyt kun on tiet tehty ja kaikki metsän viljan polut laitettu, käydään
ahkeria palkitsemaan, mutta laiskoja ja huolimattomia rankaisemaan.
Se on Tapion ja Mielikin tahto, jonka he ovat minulle tarkoin
selittäneet ja teille ilmoittaa käskeneet. Kuka teistä on nyt kovimman
työn tehnyt?»

Karhu, pitkävilla mesikämmen, siinä vielä siivoili pihkaa ja naavaa


turkistaan eikä puhunut mitään, mutta toiset huusivat:

»Karhu on kovimman työn tehnyt. Hän on oikea metsän


nurinkääntäjä! Ja väkevä!»

Mutta silloinpa kuului maasta pientä ja jäntevätä ääntä.


Muurahainen se sieltä todisti:

»Minä elävistä kokooni nähden vankin olen. Olen varmasti!


Kokoiseni kiven nostan helposti leuvoillani. Koettakoonpa kontio sitä
konstia. Vaikka puuhun sen vien!»

»No kaikkia tässä vielä!» mörähti karhu halveksivasti, mutta muut


eläimet rupesivat muurahaista puolustamaan:

»Totta puhuu! Väkevä on kokoonsa nähden tämä pieni mies. Ota,


kontio, kivi tuosta suuhusi ja koeta viedä se puuhun. Sittenpähän
nähdään!»

Koetti karhu hamuilla leukoihinsa suurta kokoistansa kiveä, mutta


eihän siitä mitään tullut. Hänen siinä äkäisenä yrittäessään piukasi
muurahainen jo männyn latvasta, että täällä ollaan, ja pudotti sinne
viemänsä kiven karhun niskaan sekä tuli alas. Mutta karhu suuttui ja
roppasi pikku miestä suurella kämmenellä selkään, niin että toinen
aivan hävisi hiekkaan. Säikähtyneinä ruvetaan häntä hakemaan, kun
hän jo ilmestyykin hiekan sisästä kuin vedestä ja alkaa kiivaasti
kannella karhun niskaan. Mutta kettu sanoi:
»Ole vaiti! Karhu on sittenkin Metsolan väkevin urho ja siksi on
Tapio määrännyt hänet valtiaaksi Pohjolan metsiin. Kun hän
tekemillämme teillä kävelee, pitää muiden mennä tieltä pois.»

Äkäinen muurahainen laahautui puolivaivaisena ketun eteen ja


valitti:

»Kaikki pienet rikat minä olen nokkinut ja kantanut pois ja nyt


minua karhu tällä tavalla runteli! Mitä minä nyt teen?»

Kettu vastasi lohduttaen:

»Saat niitä rikkoja kantaa koko ikäsi ja niistä tehdä talosi ja


kartanosi. Ja minkä vain yhdytät tielläsi, niin sen saat tappaa, jos
voitat. Ja me keitämme sinulle vielä sellaista väkevää voidetta, että
silmät sokaiset kun sitä vain päälle ruiskutat. Ja heinänkorren verran
me sinulle vielä annamme voimaa lisäksi, että tosiaankin olisit
kaikista elävistä kokoosi nähden vankin mies.»

Rauhoittui siitä muurahainen, mutta karhu mulkoili häneen


vihastuneena kämmentensä takaa ja päätti syödä hänet missä
tapaa, koska rupesi hänen kanssaan voimista kilpailemaan. Ja
niinpä karhu vielä tänäkin päivänä syöpi muurahaisen missä vain
yhdyttää.

*****

Jakaa kettu edelleen niitä Tapion palkinnoita. Tulee saapuville


hukka, irvihuuli, ja työssä on suunsa revennyt suureksi ja poskinahat
vyyhdellä riippuvat. Ulisee hukka, että mitä hänelle Mikko antaa
palkinnoksi. Mikko toimittaa, että ota mitä saat syödäksesi,
lammasta, sikaa, vuohta. Hukka siihen mukautuu: »No, omianipa
Welcome to our website – the ideal destination for book lovers and
knowledge seekers. With a mission to inspire endlessly, we offer a
vast collection of books, ranging from classic literary works to
specialized publications, self-development books, and children's
literature. Each book is a new journey of discovery, expanding
knowledge and enriching the soul of the reade

Our website is not just a platform for buying books, but a bridge
connecting readers to the timeless values of culture and wisdom. With
an elegant, user-friendly interface and an intelligent search system,
we are committed to providing a quick and convenient shopping
experience. Additionally, our special promotions and home delivery
services ensure that you save time and fully enjoy the joy of reading.

Let us accompany you on the journey of exploring knowledge and


personal growth!

textbookfull.com

You might also like