Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 14

Regex Construction for Pattern Matching

Recommended resources

Hello! Welcome back to our exploration of the Theory of Computation.

In our last lesson, we formally defined regular expressions as a way to describe languages using the operations of union, concatenation, and Kleene star. We saw that every regular expression corresponds to a language .

Today, we shift from interpreting regular expressions to creating them. Your goal for this lesson is to learn how to construct regular expressions for languages with specified patterns. This is the skill that bridges the gap between an abstract language description and a concrete, formal representation. Given your background in software development, especially in areas like NLP and building parsers, you've likely used regex libraries for practical pattern matching. This lesson will solidify the theoretical principles behind that practice.

A Strategy for Building Regular Expressions

Constructing a regular expression is a bit like programming: it’s a creative process of problem-solving. A good general strategy is to deconstruct the language description into its fundamental pieces and then translate those pieces into regex syntax.

  1. Analyze the Pattern: Read the description of the language carefully. Break it down into smaller, manageable constraints.
    • What are the required, fixed parts of the strings (e.g., must start with 01, must contain aa)? These often map to concatenation.
    • Are there choices or alternatives (e.g., starts with a or b)? This implies union (∪).
    • Are parts of the string repeated zero or more times (e.g., "any number of a's")? This calls for the Kleene star (*).
    • Are parts repeated one or more times? This can be handled with positive closure, R⁺, which is shorthand for RR*.
  2. Translate and Combine: Convert each constraint into a small regular expression. Then, combine these smaller expressions using concatenation, union, and star operators to build the final expression. Use parentheses () to enforce the correct order of operations.
  3. Test and Refine: Check your regular expression against some sample strings that should be in the language (positive examples) and some that should not (negative examples). This often reveals edge cases, like the empty string , that you might have missed.

Common Patterns and Their Construction

Let's walk through some of the most common types of patterns. We'll use a video that provides numerous examples. For each pattern type, I'll explain the logic and then direct you to the relevant part of the video to see it in action.

Regular Expression Examples || Theory of Computation || Automata Theory || FLAT || Design a

This video by Sudhakar Atchala presents 15 excellent examples of constructing regular expressions. We will use it as a practical guide for different pattern types. You don't need to watch it all at once; we will refer to specific examples as we go.

Keep this video open and jump to the timestamps mentioned below as you progress through the lesson. The examples are quick and clear.

Let's assume our alphabet is or unless stated otherwise. The expression will be our go-to for "any string of a's and b's".

1. Patterns Based on Prefixes, Suffixes, and Substrings

These are some of the most straightforward patterns.

  • Starts with a pattern w: The string must begin with w, followed by anything.

    • Regex: w(a \cup b)^*
    • Example (starts with 1, ends with 0): Watch the video from 2:02 to 2:37 (Example 3). The logic is: 1 (starts with 1) (0 \cup 1)^* (followed by anything) 0 (ends with 0). The resulting regex is 1(0 \cup 1)^*0.
  • Ends with a pattern w: The string must have anything at the beginning, but must end with w.

    • Regex: (a \cup b)^*w
    • Example (ends with 00): Watch the video from 1:19 to 2:02 (Example 2). Regex: (0 \cup 1)^*00.
  • Contains a pattern w as a substring: w can appear anywhere in the string.

    • Regex: (a \cup b)^*w(a \cup b)^*
    • Example (contains the substring aaa): Watch the video from 10:41 to 11:04 (Example 14). Regex: (a \cup b)^*aaa(a \cup b)^*.

2. Patterns Based on Positional Constraints

Sometimes the position of a character is specified relative to an end of the string.

  • The k-th character from the right is a: This means after the required a, there must be exactly k-1 more characters.
    • Regex Logic: (anything) a (char) (char) ... (k-1 times)
    • Example (3rd character from the right is a): Watch the video from 5:31 to 6:19 (Example 7). The regex is (a \cup b)^*a(a \cup b)(a \cup b).
Test your understanding!

Let . Construct a regular expression for the language of all strings where the second symbol is a 0 and the string contains the substring 11.

Show answer

There are two cases to consider, which suggests a union:

  1. The 11 substring appears after the second position. The string starts with any symbol, followed by 0. After that, anything can come, as long as it contains 11.

    • Regex: (0 \cup 1)0(0 \cup 1)^*11(0 \cup 1)^*
  2. The 11 substring overlaps with the start. This can't happen if the second symbol must be 0. The first symbol would have to be 1 and the second 1, which violates the condition.

So, the first case is the complete solution: (0 \cup 1)0(0 \cup 1)^*11(0 \cup 1)^*.

3. Patterns Based on Counting

These patterns involve constraints on the number of occurrences of a symbol.

  • At least n occurrences: The required symbol must appear n or more times, separated by any other characters.

    • Example (at least two b's): Watch the video from 6:19 to 7:12 (Example 8). The logic is to ensure two b's appear in the string, with anything allowed before, between, and after them.
    • Regex: (a \cup b)^*b(a \cup b)^*b(a \cup b)^*
  • Exactly n occurrences: This is trickier. If we want exactly two b's, we must ensure no other b's appear. This means the other parts of the string can only be a's.

    • Example (exactly two b's): Watch the video from 7:12 to 7:57 (Example 9).
    • Regex: a*ba*ba*
  • At most n occurrences: This means the string can have 0, 1, ..., or n occurrences. We handle this with a union of the "exactly" cases.

    • Example (at most two a's): Watch the video from 9:58 to 10:41 (Example 13). The logic is to combine the expressions for exactly zero a's (b*), exactly one a (b*ab*), and exactly two a's (b*ab*ab*).
    • Regex: b* \cup b*ab* \cup b*ab*ab*
  • Number of symbols is divisible by k: This is a classic, more advanced pattern. Let's design a regex for strings where the number of a's is a multiple of 3 (i.e., 0, 3, 6, ...).

    • Logic: Strings can contain any number of b's. The a's must come in groups of three. Between any two a's, there can be b's.
    • A block that adds exactly three a's is ab*ab*a.
    • We can have b's at the start: b*.
    • We can repeat groups of three a's: (b*ab*ab*a)*.
    • Combining these ideas, a robust expression is: b*(ab*ab*ab*)*. This allows for leading b's (b*), and then zero or more blocks of (ab*ab*ab*), where each block introduces exactly three a's while allowing b's in between.
    • Watch the video from 9:04 to 9:58 (Example 12) for a similar line of reasoning.

4. Patterns of Exclusion

What if a language is defined by what it cannot contain?

  • Does not contain the substring ab: This means that if an a ever appears, no b can appear after it. The only way for this to be true is if all b's come before all a's.
    • Example (doesn't contain ab): Watch the video from 11:04 to 12:02 (Example 15).
    • Regex: b*a*

Practical Shorthands and Real-World Examples

In practice, and in some textbooks, you'll see shorthand notations to make regular expressions more concise. You've probably used these in your coding work.

Regular Expressions

These lecture slides from Stanford nicely summarize the common shorthands used in regular expressions. This connects our formal notation to what you see in programming.

Read the slide titled 'Shorthand Summary'. It introduces R⁺, R?, and Σ.

Here's a quick summary of the most important shorthands:

  • R⁺ (Positive Closure): One or more occurrences of R. It's shorthand for RR*.
  • R? (Zero or one): Zero or one occurrence of R. It's shorthand for R ∪ ε.

The patterns we've discussed form the basis for recognizing everything from programming language syntax to file formats. The image below shows how these concepts are applied to define patterns for real-world data.

Real-World Applications of Regular Expressions
This table provides examples of regular expressions for practical applications, such as Java identifiers, email addresses, and phone numbers. This illustrates the direct relevance of constructing precise patterns.

Conclusion

In this lesson, you learned to translate descriptive language requirements into formal regular expressions. This is a fundamental skill that requires breaking a problem down and methodically applying the core operators of union, concatenation, and Kleene star.

Key Takeaways:

  • Decomposition is key: Break down complex language descriptions into smaller, manageable constraints.
  • Master the building blocks: Understand how to represent concepts like "starts with," "contains," "at least n," and "exactly n" using combinations of ∪, *, and concatenation.
  • Handle tricky cases systematically: Patterns like "at most n," "divisible by k," and "does not contain" often require more careful construction using unions or thinking about what is allowed.
  • Theory meets practice: The formal regular expressions we build here are the theoretical foundation for the powerful pattern-matching tools used in nearly every area of computer science.

Preview of the Next Lesson:
We've now seen two different ways to represent regular languages: finite automata (which recognize strings) and regular expressions (which describe strings). Are they equally powerful? The answer is yes, and proving it is a cornerstone of this course.

In the next lesson, we will begin building the bridge from regular expressions to automata. We will introduce a new type of automaton, the NFA with -transitions (NFA-), which is a crucial intermediate step in converting any regular expression into an equivalent finite automaton.