Code Logo

Celestial Pattern Synthesis

Published at05 Jan 2026
Hard 2 views
Like2

You are given a target mural string and a list of reusable snippets. Each snippet has both some text and a cost. The goal is to build the mural exactly, from left to right, with the smallest total cost.

If several snippets can match the current position, you are free to choose any of them, and you may reuse the same snippet multiple times. Besides the minimum total cost, you also need to return one valid sequence of snippet indices that achieves that cost.

For example, if mural = "ababa" and snippets = [("ab",3),("aba",4),("ba",2)], one minimum-cost answer is cost = 6, sequence = [2,3]. That means use snippet 2 first, then snippet 3. If no sequence of snippets can build the mural exactly, the answer should be cost = -1, sequence = [].

So the task is to cover the full mural with matching snippets while minimizing the total cost and keeping enough information to reconstruct one cheapest sequence.

Example Input & Output

Example 1
Input
mural = "ababa", snippets = [("ab",3),("aba",4),("ba",2)]
Output
cost = 6, sequence = [2, 3]
Explanation

Example 1: Minimal cost solution using snippets at indices 2 and 3

Example 2
Input
mural = "aaaa", snippets = [("aa",3),("a",2)]
Output
cost = 6, sequence = [2, 2, 2, 2]
Explanation

Example 2: Using snippet at index 2 four times

Example 3
Input
mural = "abc", snippets = [("ac",5),("bc",4)]
Output
cost = -1, sequence = []
Explanation

Example 3: No valid solution exists

Algorithm Flow

Recommendation Algorithm Flow for Celestial Pattern Synthesis
Recommendation Algorithm Flow for Celestial Pattern Synthesis

Solution Approach

This is a weighted version of word break, so dynamic programming fits nicely. Let dp[i] mean the minimum cost needed to build the suffix starting at position i in the mural.

At position i, try every snippet that matches the mural starting there. If a snippet of length len matches, then it can transition to:

dp[i] = min(dp[i], snippetCost + dp[i + len])

The base case is dp[mural.length] = 0, because once we reach the end, no more cost is needed.

To reconstruct the sequence, we store which snippet gave the best answer at each position. After the DP finishes, we can walk forward from index 0 and follow those saved choices.

If dp[0] never becomes finite, then the mural cannot be built and we return -1 with an empty sequence. Otherwise, dp[0] is the minimum cost, and the recorded choices give us one optimal snippet sequence.

Best Answers

java
import java.util.*;

class Solution {
    public Object[] celestial_pattern_synthesis(String mural, List<Object[]> snippets) {
        if (mural == null || mural.isEmpty()) return new Object[]{0L, new ArrayList<Integer>()};
        int N = mural.length();
        int M = snippets.size();
        List<Occ>[] occurrences = new List[M];
        for (int i = 0; i < M; i++) {
            occurrences[i] = new ArrayList<>();
            String pat = (String) snippets.get(i)[0];
            int start = 0;
            while ((start = mural.indexOf(pat, start)) != -1) {
                occurrences[i].add(new Occ(start, start + pat.length()));
                start++;
            }
        }
        long[] dp = new long[N + 1];
        Arrays.fill(dp, Long.MAX_VALUE);
        dp[N] = 0;
        List<Match>[] byEnd = new List[N + 1];
        for (int i = 0; i <= N; i++) byEnd[i] = new ArrayList<>();
        for (int i = 0; i < M; i++) {
            int cost = ((Number) snippets.get(i)[1]).intValue();
            for (Occ occ : occurrences[i]) byEnd[occ.end].add(new Match(occ.start, cost));
        }
        PriorityQueue<PQItem> pq = new PriorityQueue<>((a, b) -> Long.compare(a.val, b.val));
        for (int i = N; i > 0; i--) {
            if (dp[i] != Long.MAX_VALUE) {
                for (Match m : byEnd[i]) pq.add(new PQItem(m.cost + dp[i], m.start));
            }
            while (!pq.isEmpty() && pq.peek().start > i - 1) pq.poll();
            if (!pq.isEmpty()) dp[i - 1] = pq.peek().val;
        }
        if (dp[0] == Long.MAX_VALUE) return new Object[]{-1L, new ArrayList<Integer>()};
        List<Integer> resSeq = new ArrayList<>();
        int curr = 0;
        while (curr < N) {
            int bestIdx = Integer.MAX_VALUE;
            int bestEnd = -1;
            for (int j = 0; j < M; j++) {
                int cost = ((Number) snippets.get(j)[1]).intValue();
                int idx = j + 1;
                if (idx > bestIdx) continue;
                for (Occ occ : occurrences[j]) {
                    if (occ.start <= curr && occ.end > curr) {
                        if (dp[occ.end] != Long.MAX_VALUE && cost + dp[occ.end] == dp[curr]) {
                            if (idx < bestIdx) {
                                bestIdx = idx;
                                bestEnd = occ.end;
                            } else {
                                bestEnd = Math.max(bestEnd, occ.end);
                            }
                            break;
                        }
                    }
                }
            }
            resSeq.add(bestIdx);
            curr = bestEnd;
        }
        return new Object[]{dp[0], resSeq};
    }
    static class Occ { int start, end; Occ(int s, int e) { start = s; end = e; } }
    static class Match { int start, cost; Match(int s, int c) { start = s; cost = c; } }
    static class PQItem { long val; int start; PQItem(long v, int s) { val = v; start = s; } }
}