All Euler problems
Project Euler

Freefarea

Let S = {A, E, F, R}. For n >= 0, let S^(n) denote the set of words of length n consisting of letters from S. The four keywords are: FREE, FARE, AREA, and REEF. Let f(n) be the number of words in S...

Source sync Apr 19, 2026
Problem #0679
Level Level 14
Solved By 1,100
Languages C++, Python
Answer 644997092988678
Length 397 words
dynamic_programminggeometrylinear_algebra

Problem Statement

This archive keeps the full statement, math, and original media on the page.

Let \(S\) be the set consisting of the four letters \(\{\texttt {`A'},\texttt {`E'},\texttt {`F'},\texttt {`R'}\}\).

For \(n\ge 0\), let \(S^*(n)\) denote the set of words of length \(n\) consisting of letters belonging to \(S\).

We designate the words \(\texttt {FREE}, \texttt {FARE}, \texttt {AREA}, \texttt {REEF}\) as keywords.

Let \(f(n)\) be the number of words in \(S^*(n)\) that contains all four keywords exactly once.

This first happens for \(n=9\), and indeed there is a unique 9 lettered word that contain each of the keywords once: \(\texttt {FREEFAREA}\)

So, \(f(9)=1\).

You are also given that \(f(15)=72863\).

Find \(f(30)\).

Problem 679: Freefarea

Mathematical Analysis

Approach: Aho-Corasick Automaton + Dynamic Programming

This is a classic string combinatorics problem that can be solved by combining the Aho-Corasick multi-pattern matching automaton with dynamic programming.

Key Idea: Build an Aho-Corasick automaton from the four keywords. The automaton tracks partial matches of all keywords simultaneously. We augment the DP state with a bitmask recording which keywords have been fully matched.

State Space

  • Automaton node vv: encodes the longest suffix of the current string that is a prefix of some keyword. The trie has approximately 13 nodes.
  • Bitmask m{0,1,,15}m \in \{0, 1, \ldots, 15\}: bit ii is set if keyword ii has been seen exactly once.

A transition on character cc from state (v,m)(v, m):

  1. Compute the new automaton node v=δ(v,c)v' = \delta(v, c).
  2. Check the output function out(v)\text{out}(v') — a bitmask of keywords ending at vv'.
  3. If out(v)m\text{out}(v') \cap m \ne \emptyset, discard this transition (a keyword would appear twice).
  4. Otherwise, transition to (v,mout(v))(v', m \cup \text{out}(v')).

Final Count

f(n)=vm=15dp[v][m]f(n) = \sum_{\substack{v \\ m = 15}} \text{dp}[v][m]

where the sum is over all automaton nodes vv with all four keywords matched.

Editorial

Project Euler 679: Freefarea Count words of length n over {A, E, F, R} containing each of FREE, FARE, AREA, REEF exactly once. We use a DFA-based DP approach. Track: Since we need exactly one occurrence of each, we track a bitmask of which keywords have appeared, and reject any state where a keyword appears a second time. Keywords: FREE, FARE, AREA, REEF Alphabet: {A, E, F, R} We use Aho-Corasick automaton to track all four patterns simultaneously. We build the Aho-Corasick trie from the four keywords. We then compute failure links and output functions via BFS. Finally, run DP for n=30n = 30 steps, transitioning through all 4 characters at each step.

Pseudocode

Build the Aho-Corasick trie from the four keywords
Compute failure links and output functions via BFS
Run DP for $n = 30$ steps, transitioning through all 4 characters at each step
Sum counts over all states with mask $= 15$

Correctness

Theorem. The method described above computes exactly the quantity requested in the problem statement.

Proof. The preceding analysis identifies the admissible objects and derives the formula, recurrence, or exhaustive search carried out by the algorithm. The computation evaluates exactly that specification, so every valid contribution is included once and no invalid contribution is counted. Therefore the returned value is the required answer. \square

Complexity Analysis

  • States: O(V24)=O(1316)=O(208)O(|V| \cdot 2^4) = O(13 \cdot 16) = O(208) states.
  • Transitions per state: 4 characters.
  • Steps: 30.
  • Total: O(302084)=O(24960)O(30 \cdot 208 \cdot 4) = O(24960) operations.

Answer

644997092988678\boxed{644997092988678}

Code

Each problem page includes the exact C++ and Python source files from the local archive.

C++ project_euler/problem_679/solution.cpp
#include <bits/stdc++.h>
using namespace std;

// Aho-Corasick automaton for multiple pattern matching
struct AhoCorasick {
    vector<map<char, int>> go;
    vector<int> fail, out;

    AhoCorasick() {
        go.push_back({});
        fail.push_back(0);
        out.push_back(0);
    }

    void addPattern(const string& s, int idx) {
        int cur = 0;
        for (char ch : s) {
            if (go[cur].find(ch) == go[cur].end()) {
                go[cur][ch] = go.size();
                go.push_back({});
                fail.push_back(0);
                out.push_back(0);
            }
            cur = go[cur][ch];
        }
        out[cur] |= (1 << idx);
    }

    void build(const string& alpha) {
        queue<int> q;
        for (char ch : alpha) {
            if (go[0].count(ch)) {
                fail[go[0][ch]] = 0;
                q.push(go[0][ch]);
            } else {
                go[0][ch] = 0;
            }
        }
        while (!q.empty()) {
            int r = q.front(); q.pop();
            for (char ch : alpha) {
                if (go[r].count(ch)) {
                    int s = go[r][ch];
                    q.push(s);
                    int st = fail[r];
                    while (st && !go[st].count(ch)) st = fail[st];
                    fail[s] = go[st].count(ch) ? go[st][ch] : 0;
                    if (fail[s] == s) fail[s] = 0;
                    out[s] |= out[fail[s]];
                } else {
                    int st = fail[r];
                    while (st && !go[st].count(ch)) st = fail[st];
                    go[r][ch] = go[st].count(ch) ? go[st][ch] : 0;
                }
            }
        }
    }
};

int main() {
    vector<string> keywords = {"FREE", "FARE", "AREA", "REEF"};
    string alpha = "AEFR";
    int N = 30;

    AhoCorasick ac;
    for (int i = 0; i < 4; i++) ac.addPattern(keywords[i], i);
    ac.build(alpha);

    int numNodes = ac.go.size();

    // dp[node][mask] = count
    map<pair<int,int>, long long> dp;
    dp[{0, 0}] = 1;

    for (int step = 0; step < N; step++) {
        map<pair<int,int>, long long> ndp;
        for (auto& [state, cnt] : dp) {
            auto [node, mask] = state;
            for (char ch : alpha) {
                int nn = ac.go[node][ch];
                int no = ac.out[nn];
                if (no & mask) continue; // keyword seen twice
                int nm = mask | no;
                ndp[{nn, nm}] += cnt;
            }
        }
        dp = ndp;
    }

    long long ans = 0;
    for (auto& [state, cnt] : dp) {
        if (state.second == 15) ans += cnt;
    }

    cout << ans << endl;
    return 0;
}