Consider the infinite concatenation
$$C=12345678910111213\dots$$
For a given \(n\), let \(P=\text{str}(n)\). Define \(f(n)\) as the starting position of the \(n\)-th occurrence of \(P\) inside \(C\). The Project Euler sum is taken over \(n=3^k\).
The pattern \(P\) can appear:
1. entirely inside one integer,
2. across the boundary between consecutive integers,
3. overlapping with itself.
So we must search in the continuous digit stream \(123456789101112\dots\), not number by number. Resetting the match state at integer boundaries would miss valid occurrences.
The code builds a Knuth-Morris-Pratt automaton for the decimal pattern \(P\). A state \(q\in\{0,\dots,L\}\), where \(L=|P|\), means: “the last processed digits currently match the first \(q\) digits of \(P\)”.
For every state \(q\) and digit \(d\in\{0,\dots,9\}\), the automaton stores:
$$q'=\delta(q,d),$$
and an output bit
$$o(q,d)\in\{0,1\},$$
which tells whether reading digit \(d\) completes one new occurrence of \(P\).
Because the KMP fallback is applied after a full match, overlapping occurrences are counted correctly.
For any finite digit string \(w\), define an operator \(T_w\). Starting from automaton state \(q\), this operator tells us two things after scanning \(w\):
$$T_w(q)=\bigl(q_{\text{end}}(q),c_w(q)\bigr),$$
where \(q_{\text{end}}(q)\) is the ending state and \(c_w(q)\) is the number of new matches found while reading \(w\).
This is exactly what the code stores as end_state and add_count.
If a block is split as \(w=uv\), then scanning \(u\) first and \(v\) second gives
$$T_{uv}(q)=\Bigl(q_v(q_u(q)),\,c_u(q)+c_v\bigl(q_u(q)\bigr)\Bigr).$$
So composition is associative. This is the central algebraic fact used by the solver: once we know the operator of a block, we never need to rescan its digits.
Let a decimal prefix already be fixed. Then:
1. full_block(prefix, rem_digits) means the concatenation of all numbers with that prefix followed by all \(10^{\text{rem\_digits}}\) possible suffixes;
2. partial_block(prefix, rem_digits, upper_suffix) means the same, but only up to a given suffix bound.
These operators are memoized. Since many queries reuse the same prefix-and-length structure, caching removes the exponential blowup.
To process all integers from \(1\) to \(N\), the code does not concatenate them explicitly. Instead it composes operators in this order:
1. all complete digit-length blocks with fewer digits than \(N\);
2. within the top digit length, all complete leading-digit blocks smaller than the first digit of \(N\);
3. one final partial block for the prefix that leads exactly up to \(N\).
The result is an operator we may denote by
$$T_{[1..N]}.$$
Applying it to automaton state \(0\) yields the total number of occurrences of \(P\) in the finite prefix \(123\dots N\).
Define
$$A(N)=\text{number of occurrences of }P\text{ in }123\dots N.$$
Clearly \(A(N)\) is monotone nondecreasing in \(N\). Therefore we can binary-search the smallest \(N\) such that
$$A(N)\ge n.$$
That identifies the exact integer in which the \(n\)-th occurrence finishes.
After binary search finds this minimal \(N\), the code computes the automaton state after scanning \(123\dots(N-1)\). Then it scans the digits of \(N\) once more, counting only the matches produced inside that last number.
If the desired occurrence ends at digit index \(i\) inside \(\text{str}(N)\), and \(L=|P|\), then its global starting position is
$$\text{digits\_before}(N-1)+i-L+2.$$
Here
$$\text{digits\_before}(N)=\sum_{d=1}^{D-1}9\cdot 10^{d-1}\cdot d+\bigl(N-10^{D-1}+1\bigr)D,$$
where \(D\) is the number of digits of \(N\).
The implementation verifies several known values:
$$f(1)=1,\qquad f(5)=81,\qquad f(12)=271,\qquad f(7780)=111111365.$$
It also brute-forces all cases \(1\le n\le 30\) against a direct stream simulation, which is a strong validation of the operator approach.
The class PatternEngine does all heavy lifting. It builds the KMP automaton, interns operators so identical operators are reused, memoizes operator composition, and provides:
1. range_operator(N) for the whole prefix \(123\dots N\),
2. occ_and_state(N) for total match count and ending automaton state,
3. nth_occurrence_position(target) for the final binary search and local scan.
The outer solver simply evaluates this engine for patterns \(3,9,27,\dots,3^{13}\) and sums the returned positions.
If \(L=|P|\), one operator composition costs \(O(L)\). Thanks to memoized full/partial blocks, evaluating \(A(N)\) is much closer to polylogarithmic in \(N\) than to linear in the total digit stream length. That is the whole reason the problem is feasible: the code never generates the massive prefix of \(C\) explicitly.
Betrachte die unendliche Konkatenation
$$C=12345678910111213\dots$$
Für ein gegebenes \(n\) sei \(P=\text{str}(n)\). Dann ist \(f(n)\) die Startposition des \(n\)-ten Auftretens von \(P\) in \(C\). In der Aufgabe wird über \(n=3^k\) summiert.
Das Muster \(P\) kann
1. vollständig in einer einzelnen Zahl liegen,
2. eine Grenze zwischen zwei aufeinanderfolgenden Zahlen überschreiten,
3. sich mit sich selbst überlappen.
Darum muss im gesamten Strom \(123456789101112\dots\) gesucht werden; ein Zurücksetzen an Zahlengrenzen wäre falsch.
Der Code baut einen Knuth-Morris-Pratt-Automaten für das Dezimalmuster \(P\). Ein Zustand \(q\in\{0,\dots,L\}\) mit \(L=|P|\) bedeutet, dass die zuletzt gelesenen Ziffern gerade die ersten \(q\) Zeichen von \(P\) treffen.
Für jeden Zustand \(q\) und jede Ziffer \(d\in\{0,\dots,9\}\) speichert der Automat:
$$q'=\delta(q,d)$$
und ein Ausgabebit
$$o(q,d)\in\{0,1\},$$
das anzeigt, ob beim Lesen von \(d\) ein neues vollständiges Auftreten endet. Wegen des KMP-Fallbacks nach einem Treffer werden auch überlappende Vorkommen korrekt gezählt.
Für jeden endlichen Ziffernstring \(w\) definieren wir einen Operator \(T_w\). Startet man im Zustand \(q\), dann liefert
$$T_w(q)=\bigl(q_{\text{end}}(q),c_w(q)\bigr)$$
den Endzustand und die Anzahl neuer Treffer beim Einlesen von \(w\). Genau dies wird im Code als end_state und add_count gespeichert.
Zerfällt ein Block in \(w=uv\), dann gilt
$$T_{uv}(q)=\Bigl(q_v(q_u(q)),\,c_u(q)+c_v\bigl(q_u(q)\bigr)\Bigr).$$
Die Komposition ist also assoziativ. Das ist die zentrale algebraische Beobachtung: kennt man den Operator eines Blocks, muss man seine Ziffern nie wieder explizit scannen.
Ist bereits ein Dezimalpräfix fest vorgegeben, dann bedeutet:
1. full_block(prefix, rem_digits): alle Zahlen mit diesem Präfix und allen möglichen Suffixen der verbleibenden Länge;
2. partial_block(prefix, rem_digits, upper_suffix): dieselbe Struktur, aber nur bis zu einer gegebenen Suffix-Grenze.
Diese Operatoren werden memoisiert. Da viele Anfragen dieselbe Präfix-Längen-Struktur wiederverwenden, vermeidet das die kombinatorische Explosion.
Um alle Zahlen von \(1\) bis \(N\) zu verarbeiten, konkatenieren wir sie nicht explizit. Stattdessen setzt der Code die Operatoren in folgender Reihenfolge zusammen:
1. alle vollständigen Stellenzahlblöcke mit weniger Stellen als \(N\);
2. in der obersten Stellenzahl alle vollständigen Führungsziffern-Blöcke unterhalb der ersten Ziffer von \(N\);
3. ein letzter partieller Block, der exakt bis \(N\) reicht.
Das Resultat ist ein Operator
$$T_{[1..N]},$$
und angewandt auf Zustand \(0\) liefert er die Gesamtzahl der Vorkommen von \(P\) im endlichen Präfix \(123\dots N\).
Definiere
$$A(N)=\text{Anzahl der Vorkommen von }P\text{ in }123\dots N.$$
Offensichtlich ist \(A(N)\) monoton nicht fallend. Daher kann man per Binärsuche das kleinste \(N\) finden mit
$$A(N)\ge n.$$
Dieses \(N\) ist genau die Zahl, in der das \(n\)-te Auftreten endet.
Nachdem diese minimale Zahl \(N\) gefunden ist, bestimmt der Code zunächst den Automatenzustand nach dem Lesen von \(123\dots(N-1)\). Dann werden die Ziffern von \(N\) einmal lokal abgescannt, bis das gesuchte Auftreten erreicht ist.
Endet der Treffer an der Ziffer mit lokalem Index \(i\) in \(\text{str}(N)\) und ist \(L=|P|\), so lautet die globale Startposition
$$\text{digits\_before}(N-1)+i-L+2.$$
Dabei ist
$$\text{digits\_before}(N)=\sum_{d=1}^{D-1}9\cdot 10^{d-1}\cdot d+\bigl(N-10^{D-1}+1\bigr)D,$$
wobei \(D\) die Stellenzahl von \(N\) ist.
Die Implementierung überprüft unter anderem:
$$f(1)=1,\qquad f(5)=81,\qquad f(12)=271,\qquad f(7780)=111111365.$$
Zusätzlich werden alle Fälle \(1\le n\le 30\) gegen eine direkte Brute-Force-Stromsimulation geprüft.
Die Klasse PatternEngine erledigt die gesamte schwere Arbeit. Sie baut den KMP-Automaten, interned identische Operatoren, memoisiert Kompositionen und stellt bereit:
1. range_operator(N) für das Präfix \(123\dots N\),
2. occ_and_state(N) für Trefferzahl und Endzustand,
3. nth_occurrence_position(target) für Binärsuche plus lokalen Endscan.
Der äußere Solver wendet dies nur noch auf die Muster \(3,9,27,\dots,3^{13}\) an und summiert die Positionen.
Für \(L=|P|\) kostet eine Operator-Komposition \(O(L)\). Dank memoisierten Voll-/Teilblöcken ist die Auswertung von \(A(N)\) viel näher an polylogarithmischem Verhalten in \(N\) als an linearer Stromsimulation. Genau das macht die Aufgabe lösbar: das riesige Präfix von \(C\) wird nie explizit erzeugt.
Sonsuz birleştirme dizisini düşünelim:
$$C=12345678910111213\dots$$
Verilen bir \(n\) için \(P=\text{str}(n)\) olsun. \(f(n)\), \(P\) deseninin \(C\) içindeki \(n\). geçişinin başlangıç konumudur. Euler toplamı \(n=3^k\) değerleri üzerinden alınır.
Desen \(P\)
1. tek bir sayının tamamen içinde,
2. ardışık iki sayının sınırını aşarak,
3. kendi kendisiyle örtüşerek
görünebilir. Bu yüzden arama, sayıları tek tek ayırarak değil, bütün \(123456789101112\dots\) akışı üzerinde yapılmalıdır.
Kod, ondalık desen \(P\) için bir KMP otomatı kurar. \(L=|P|\) olmak üzere \(q\in\{0,\dots,L\}\) durumu, şu ana kadar okunan son rakamların \(P\)'nin ilk \(q\) karakteriyle eşleştiği anlamına gelir.
Her durum \(q\) ve her rakam \(d\in\{0,\dots,9\}\) için şu bilgiler tutulur:
$$q'=\delta(q,d),$$
ve bir çıktı biti
$$o(q,d)\in\{0,1\},$$
bu bit, \(d\) okununca yeni bir tam eşleşme tamamlanıp tamamlanmadığını söyler. KMP geri düşüşü sayesinde örtüşen eşleşmeler de doğru sayılır.
Her sonlu rakam dizisi \(w\) için bir \(T_w\) operatörü tanımlarız. Başlangıç durumu \(q\) iken
$$T_w(q)=\bigl(q_{\text{son}}(q),c_w(q)\bigr)$$
değeri, blok bittiğinde ulaşılan durumu ve bu blok okunurken üretilen yeni eşleşme sayısını verir. Koddaki end_state ve add_count tam olarak bunu temsil eder.
Eğer \(w=uv\) ise
$$T_{uv}(q)=\Bigl(q_v(q_u(q)),\,c_u(q)+c_v\bigl(q_u(q)\bigr)\Bigr).$$
Yani bileşim birleşmelidir. Bu gözlem kritiktir: bir bloğun operatörünü bildikten sonra onun rakamlarını tekrar tek tek okumaya gerek kalmaz.
Ondalık bir önek sabitlendiğinde:
1. full_block(prefix, rem_digits), bu önekten sonra kalan tüm olası son eklerin oluşturduğu bloktur;
2. partial_block(prefix, rem_digits, upper_suffix), aynı yapının sadece belirli bir son eke kadar olan kısmıdır.
Bu operatörler memoize edilir. Aynı önek-uzunluk yapıları çok kez tekrarlandığı için cache olmadan çözüm patlar.
\(1\)'den \(N\)'ye kadar tüm sayıları gerçekten yan yana yazmak yerine kod şu sırayla operatörleri birleştirir:
1. \(N\)'den daha kısa tüm basamak uzunluğu blokları,
2. en üst uzunlukta, \(N\)'in ilk rakamından küçük tüm tam önek blokları,
3. tam olarak \(N\)'ye kadar giden son bir kısmi blok.
Bunun sonucu
$$T_{[1..N]}$$
operatörüdür. Başlangıç durumu \(0\)'a uygulanınca, \(123\dots N\) sonlu önekindeki toplam eşleşme sayısını verir.
Şunu tanımlayalım:
$$A(N)=123\dots N\text{ içinde }P\text{ deseninin geçiş sayısı.}$$
Bu fonksiyon açıkça monoton artandır. O hâlde ikili arama ile
$$A(N)\ge n$$
şartını sağlayan en küçük \(N\) bulunabilir. Bu \(N\), aradığımız \(n\). geçişin tamamlandığı sayıdır.
Bu minimal \(N\) bulunduktan sonra kod, önce \(123\dots(N-1)\) okunduktan sonraki otomat durumunu çıkarır. Sonra sadece \(N\)'in rakamlarını bir kez daha tarar ve istenen geçişin bu son sayı içinde hangi noktada oluştuğunu bulur.
Eğer geçiş, \(\text{str}(N)\) içinde yerel indeks \(i\)'de bitiyor ve \(L=|P|\) ise, global başlangıç konumu
$$\text{digits\_before}(N-1)+i-L+2$$
olur. Burada
$$\text{digits\_before}(N)=\sum_{d=1}^{D-1}9\cdot 10^{d-1}\cdot d+\bigl(N-10^{D-1}+1\bigr)D$$
formülü, \(N\)'den önce gelen toplam rakam sayısını verir; \(D\), \(N\)'in basamak sayısıdır.
Uygulama şu bilinen değerleri doğrular:
$$f(1)=1,\qquad f(5)=81,\qquad f(12)=271,\qquad f(7780)=111111365.$$
Ayrıca \(1\le n\le 30\) için sonuçlar doğrudan brute-force akış simülasyonuyla karşılaştırılır.
PatternEngine sınıfı tüm ağır işi yapar. KMP otomatını kurar, aynı operatörleri tekrar kullanmak için intern eder, bileşimleri cache'ler ve şu arayüzleri sunar:
1. range_operator(N) ile \(123\dots N\) önekinin operatörü,
2. occ_and_state(N) ile toplam geçiş sayısı ve bitiş durumu,
3. nth_occurrence_position(target) ile ikili arama + son lokal tarama.
Dış çözücü ise bunu yalnızca \(3,9,27,\dots,3^{13}\) desenleri için çağırıp konumları toplar.
\(L=|P|\) için tek bir operatör bileşimi \(O(L)\) maliyetlidir. Memoized tam/kısmi bloklar sayesinde \(A(N)\) değerlendirmesi, \(N\)'de doğrusal olmaktan çok polilogaritmik davranışa yaklaşır. Problemi çözülebilir yapan şey tam olarak budur: \(C\)'nin devasa öneki hiçbir zaman açıkça üretilmez.
Consideremos la concatenación infinita
$$C=12345678910111213\dots$$
Para un \(n\) dado, sea \(P=\text{str}(n)\). Entonces \(f(n)\) es la posición inicial de la \(n\)-ésima aparición de \(P\) dentro de \(C\). En el problema se suman los casos \(n=3^k\).
El patrón \(P\) puede aparecer:
1. completamente dentro de un número,
2. cruzando la frontera entre dos números consecutivos,
3. solapándose consigo mismo.
Por eso hay que trabajar sobre el flujo continuo \(123456789101112\dots\), no reiniciar la búsqueda al pasar de un entero al siguiente.
El código construye un autómata KMP para el patrón decimal \(P\). Un estado \(q\in\{0,\dots,L\}\), con \(L=|P|\), significa que los últimos dígitos leídos coinciden con los primeros \(q\) caracteres de \(P\).
Para cada estado \(q\) y cada dígito \(d\in\{0,\dots,9\}\), el autómata guarda
$$q'=\delta(q,d)$$
y un bit de salida
$$o(q,d)\in\{0,1\},$$
que indica si al leer \(d\) se completa una nueva aparición. Como después de un acierto se aplica el fallback de KMP, los solapamientos también se cuentan correctamente.
Para cualquier cadena finita de dígitos \(w\), definimos un operador \(T_w\). Si se empieza en el estado \(q\), entonces
$$T_w(q)=\bigl(q_{\text{end}}(q),c_w(q)\bigr)$$
da el estado final y el número de coincidencias nuevas halladas al recorrer \(w\). Esto es exactamente lo que el código almacena en end_state y add_count.
Si un bloque se descompone como \(w=uv\), entonces
$$T_{uv}(q)=\Bigl(q_v(q_u(q)),\,c_u(q)+c_v\bigl(q_u(q)\bigr)\Bigr).$$
La composición es asociativa. Esa es la observación algebraica central: una vez conocido el operador de un bloque, no hace falta volver a escanear sus dígitos.
Si ya hay fijado un prefijo decimal, entonces:
1. full_block(prefix, rem_digits) representa todos los números con ese prefijo y todos los sufijos posibles de la longitud restante;
2. partial_block(prefix, rem_digits, upper_suffix) representa la misma estructura pero solo hasta cierto sufijo superior.
Estos operadores se memoizan. Como muchas consultas repiten la misma estructura prefijo-longitud, el caché evita una explosión combinatoria.
Para procesar todos los enteros de \(1\) a \(N\), el código no los concatena explícitamente. En vez de eso compone operadores en este orden:
1. todos los bloques completos de longitudes menores que la de \(N\);
2. dentro de la longitud superior, todos los bloques completos con primer dígito menor que el primer dígito de \(N\);
3. un último bloque parcial que llega exactamente hasta \(N\).
El resultado puede denotarse por
$$T_{[1..N]}.$$
Aplicado al estado \(0\), produce el número total de apariciones de \(P\) en el prefijo finito \(123\dots N\).
Definimos
$$A(N)=\text{número de apariciones de }P\text{ en }123\dots N.$$
Es claro que \(A(N)\) es monótona creciente. Por tanto, se puede hacer una búsqueda binaria del menor \(N\) tal que
$$A(N)\ge n.$$
Ese \(N\) es precisamente el entero donde termina la \(n\)-ésima aparición buscada.
Una vez hallado ese \(N\) mínimo, el código calcula el estado del autómata tras leer \(123\dots(N-1)\). Luego vuelve a escanear los dígitos de \(N\) y cuenta únicamente las coincidencias producidas dentro de ese último número.
Si la coincidencia deseada termina en el índice local \(i\) dentro de \(\text{str}(N)\), y \(L=|P|\), la posición global inicial es
$$\text{digits\_before}(N-1)+i-L+2.$$
Aquí
$$\text{digits\_before}(N)=\sum_{d=1}^{D-1}9\cdot 10^{d-1}\cdot d+\bigl(N-10^{D-1}+1\bigr)D,$$
donde \(D\) es el número de cifras de \(N\).
La implementación verifica varios valores conocidos:
$$f(1)=1,\qquad f(5)=81,\qquad f(12)=271,\qquad f(7780)=111111365.$$
Además, compara por fuerza bruta todos los casos \(1\le n\le 30\) con una simulación directa del flujo.
La clase PatternEngine concentra todo el trabajo pesado. Construye el autómata KMP, reutiliza operadores idénticos, memoiza composiciones y expone:
1. range_operator(N) para el prefijo \(123\dots N\),
2. occ_and_state(N) para el conteo total y el estado final,
3. nth_occurrence_position(target) para la búsqueda binaria y el escaneo final local.
El solver exterior solo aplica este motor a los patrones \(3,9,27,\dots,3^{13}\) y suma las posiciones devueltas.
Si \(L=|P|\), una composición de operadores cuesta \(O(L)\). Gracias a la memoización de bloques completos y parciales, evaluar \(A(N)\) está mucho más cerca de un coste polilogarítmico en \(N\) que de una simulación lineal del flujo. Esa es la razón por la que el problema resulta factible: el prefijo gigantesco de \(C\) nunca se construye explícitamente.
考虑无限串
$$C=12345678910111213\dots$$
对给定 \(n\),记 \(P=\text{str}(n)\)。定义 \(f(n)\) 为 \(P\) 在 \(C\) 中第 \(n\) 次出现的起始位置。题目最后要求对 \(n=3^k\) 求和。
模式 \(P\) 可能:
1. 完全出现在某一个整数内部;
2. 跨越相邻两个整数的边界;
3. 与自身重叠出现。
所以必须把 \(123456789101112\dots\) 看成一个连续数字流,而不能在每个整数边界上把匹配状态清零。
代码为十进制模式 \(P\) 建立 KMP 自动机。若 \(L=|P|\),状态 \(q\in\{0,\dots,L\}\) 表示:当前已读入流的结尾,和 \(P\) 的前 \(q\) 个字符匹配。
对于每个状态 \(q\) 和每个数字 \(d\in\{0,\dots,9\}\),自动机保存
$$q'=\delta(q,d),$$
以及一个输出位
$$o(q,d)\in\{0,1\},$$
表示读入 \(d\) 后是否新完成了一次匹配。由于完整匹配后会做 KMP fallback,因此重叠出现也会被正确计数。
对于任意有限数字串 \(w\),定义算子 \(T_w\)。若起始状态为 \(q\),则
$$T_w(q)=\bigl(q_{\text{end}}(q),c_w(q)\bigr)$$
表示扫描完 \(w\) 之后到达的状态,以及在扫描过程中新增的匹配次数。这正是代码里的 end_state 和 add_count。
若一个块分成 \(w=uv\),那么
$$T_{uv}(q)=\Bigl(q_v(q_u(q)),\,c_u(q)+c_v\bigl(q_u(q)\bigr)\Bigr).$$
因此组合是结合的。这个代数事实是整个算法的核心:一旦知道了某个块的算子,就再也不需要重新逐位扫描它。
在某个十进制前缀已经固定后:
1. full_block(prefix, rem_digits) 表示该前缀后接所有可能的剩余位后缀;
2. partial_block(prefix, rem_digits, upper_suffix) 表示只枚举到某个上界后缀为止。
这些算子都被记忆化。因为大量查询会重复相同的前缀-长度结构,所以缓存非常关键。
为了处理从 \(1\) 到 \(N\) 的所有整数,代码并不会真的生成字符串,而是按下面顺序组合算子:
1. 所有位数小于 \(N\) 的完整位数块;
2. 在最高位数层里,所有首位小于 \(N\) 首位的完整前缀块;
3. 最后一个恰好到达 \(N\) 的部分块。
这样得到的就是
$$T_{[1..N]}.$$
把它作用到初始状态 \(0\),就得到有限前缀 \(123\dots N\) 中模式 \(P\) 的总出现次数。
定义
$$A(N)=123\dots N\text{ 中 }P\text{ 的出现次数。}$$
显然 \(A(N)\) 单调不减,因此可以二分最小的 \(N\),使得
$$A(N)\ge n.$$
这个 \(N\) 正好是第 \(n\) 次出现落入的整数。
当找到这个最小 \(N\) 之后,代码先求出扫描 \(123\dots(N-1)\) 后自动机所处的状态。然后仅再扫描一次 \(\text{str}(N)\) 的数字,数出在这个最后整数中产生的匹配。
如果目标匹配在 \(\text{str}(N)\) 的局部下标 \(i\) 结束,且 \(L=|P|\),那么全局起始位置为
$$\text{digits\_before}(N-1)+i-L+2.$$
其中
$$\text{digits\_before}(N)=\sum_{d=1}^{D-1}9\cdot 10^{d-1}\cdot d+\bigl(N-10^{D-1}+1\bigr)D,$$
\(D\) 是 \(N\) 的位数。
实现中验证了若干已知值:
$$f(1)=1,\qquad f(5)=81,\qquad f(12)=271,\qquad f(7780)=111111365.$$
另外,对所有 \(1\le n\le 30\),代码还用直接暴力流模拟做了交叉验证。
PatternEngine 类完成所有核心工作:建立 KMP 自动机、复用完全相同的算子、缓存算子组合,并提供:
1. range_operator(N):前缀 \(123\dots N\) 的整体算子;
2. occ_and_state(N):总匹配数与结束状态;
3. nth_occurrence_position(target):二分查找加最后局部扫描。
外层求解器只需要把这个引擎应用到 \(3,9,27,\dots,3^{13}\) 这些模式上并求和。
若 \(L=|P|\),一次算子组合的代价是 \(O(L)\)。借助完整块/部分块的记忆化,计算 \(A(N)\) 的成本远小于线性扫描整个数字流,更接近于关于 \(N\) 的多对数级行为。正因为从不显式构造 \(C\) 的巨大前缀,这道题才是可做的。
Рассматривается бесконечная конкатенация
$$C=12345678910111213\dots$$
Для заданного \(n\) пусть \(P=\text{str}(n)\). Тогда \(f(n)\) — это позиция начала \(n\)-го вхождения \(P\) в \(C\). В задаче суммируются значения для \(n=3^k\).
Шаблон \(P\) может:
1. полностью лежать внутри одного числа,
2. пересекать границу между соседними числами,
3. перекрываться с самим собой.
Поэтому искать нужно в едином потоке \(123456789101112\dots\), а не по отдельным числам с обнулением состояния на границе.
Код строит KMP-автомат для десятичного шаблона \(P\). Состояние \(q\in\{0,\dots,L\}\), где \(L=|P|\), означает: последние прочитанные цифры совпадают с первыми \(q\) символами шаблона.
Для каждого состояния \(q\) и цифры \(d\in\{0,\dots,9\}\) хранятся
$$q'=\delta(q,d)$$
и бит выхода
$$o(q,d)\in\{0,1\},$$
который показывает, завершилось ли новое вхождение после чтения \(d\). Благодаря KMP-fallback после полного совпадения перекрывающиеся вхождения тоже учитываются правильно.
Для любой конечной строки цифр \(w\) вводится оператор \(T_w\). Если стартовать из состояния \(q\), то
$$T_w(q)=\bigl(q_{\text{end}}(q),c_w(q)\bigr)$$
даёт конечное состояние и число новых совпадений при чтении \(w\). В коде это ровно структуры end_state и add_count.
Если блок раскладывается как \(w=uv\), то
$$T_{uv}(q)=\Bigl(q_v(q_u(q)),\,c_u(q)+c_v\bigl(q_u(q)\bigr)\Bigr).$$
Композиция ассоциативна. Это главное алгебраическое наблюдение: узнав оператор блока, мы больше никогда не обязаны перечитывать его цифры напрямую.
Если некоторый десятичный префикс уже фиксирован, то:
1. full_block(prefix, rem_digits) означает все числа с этим префиксом и всеми возможными оставшимися суффиксами;
2. partial_block(prefix, rem_digits, upper_suffix) означает ту же структуру, но только до заданной верхней границы суффикса.
Эти операторы мемоизируются. Поскольку одинаковая структура префикс-длина возникает много раз, кэш здесь принципиален.
Чтобы обработать все числа от \(1\) до \(N\), код не склеивает их явно. Вместо этого он компонирует операторы в таком порядке:
1. все полные блоки с меньшим числом цифр, чем у \(N\);
2. внутри максимальной длины — все полные блоки с первой цифрой меньше первой цифры \(N\);
3. один последний частичный блок, который доходит ровно до \(N\).
Результат можно обозначить как
$$T_{[1..N]}.$$
Применение этого оператора к стартовому состоянию \(0\) даёт общее число вхождений \(P\) в конечном префиксе \(123\dots N\).
Определим
$$A(N)=\text{число вхождений }P\text{ в }123\dots N.$$
Очевидно, \(A(N)\) монотонно не убывает. Значит, можно бинарным поиском найти минимальное \(N\), для которого
$$A(N)\ge n.$$
Именно это число \(N\) содержит завершение нужного \(n\)-го вхождения.
После нахождения минимального \(N\) код сначала вычисляет состояние автомата после чтения \(123\dots(N-1)\). Затем цифры числа \(N\) сканируются ещё один раз, и считаются только те совпадения, которые возникают внутри этого последнего числа.
Если нужное вхождение заканчивается на локальной позиции \(i\) внутри \(\text{str}(N)\), а \(L=|P|\), то глобальная начальная позиция равна
$$\text{digits\_before}(N-1)+i-L+2.$$
Здесь
$$\text{digits\_before}(N)=\sum_{d=1}^{D-1}9\cdot 10^{d-1}\cdot d+\bigl(N-10^{D-1}+1\bigr)D,$$
где \(D\) — число цифр в записи \(N\).
В реализации проверяются, в частности, такие значения:
$$f(1)=1,\qquad f(5)=81,\qquad f(12)=271,\qquad f(7780)=111111365.$$
Кроме того, все случаи \(1\le n\le 30\) сверяются с прямой brute-force симуляцией потока.
Класс PatternEngine делает всю основную работу. Он строит KMP-автомат, интернирует одинаковые операторы, мемоизирует композиции и предоставляет:
1. range_operator(N) для префикса \(123\dots N\),
2. occ_and_state(N) для общего числа вхождений и конечного состояния,
3. nth_occurrence_position(target) для бинарного поиска и финального локального прохода.
Внешний решатель просто применяет этот механизм к шаблонам \(3,9,27,\dots,3^{13}\) и суммирует найденные позиции.
Если \(L=|P|\), то одна композиция операторов стоит \(O(L)\). Благодаря мемоизации полных и частичных блоков вычисление \(A(N)\) оказывается гораздо ближе к полилогарифмическому поведению по \(N\), чем к линейной симуляции всего потока. Именно поэтому задача решаема: огромный префикс \(C\) никогда не строится явно.
ننظر إلى السلسلة اللانهائية
$$C=12345678910111213\dots$$
ولعدد معطى \(n\)، نضع \(P=\text{str}(n)\). عندئذٍ تكون \(f(n)\) هي موضع بداية الظهور رقم \(n\) للنمط \(P\) داخل \(C\). وفي المسألة يتم الجمع على القيم \(n=3^k\).
يمكن أن يظهر النمط \(P\):
1. بالكامل داخل عدد واحد،
2. عابرًا الحد بين عددين متتاليين،
3. ومتداخلًا مع نفسه.
لذلك يجب البحث في التدفق الكامل \(123456789101112\dots\)، وليس رقمًا رقمًا مع إعادة ضبط الحالة عند كل حد.
يبني الكود أوتومات KMP للنمط العشري \(P\). إذا كان \(L=|P|\)، فالحالة \(q\in\{0,\dots,L\}\) تعني أن آخر الأرقام المقروءة تطابق أول \(q\) محارف من \(P\).
ولكل حالة \(q\) ولكل رقم \(d\in\{0,\dots,9\}\) يخزن الأوتومات
$$q'=\delta(q,d)$$
وبت خرج
$$o(q,d)\in\{0,1\},$$
يشير إلى ما إذا كانت قراءة \(d\) قد أنهت تطابقًا جديدًا كاملًا. وبسبب fallback الخاص بـ KMP بعد اكتمال المطابقة، تُحسب الظهورات المتداخلة أيضًا بشكل صحيح.
لكل سلسلة أرقام منتهية \(w\) نعرّف مؤثرًا \(T_w\). إذا بدأنا من الحالة \(q\)، فإن
$$T_w(q)=\bigl(q_{\text{end}}(q),c_w(q)\bigr)$$
يعطي الحالة النهائية وعدد التطابقات الجديدة أثناء قراءة \(w\). وهذا هو بالضبط ما يمثله end_state وadd_count في الكود.
إذا كانت الكتلة \(w=uv\)، فإن
$$T_{uv}(q)=\Bigl(q_v(q_u(q)),\,c_u(q)+c_v\bigl(q_u(q)\bigr)\Bigr).$$
إذن التركيب تجميعي. وهذه هي الملاحظة الجبرية الأساسية: بمجرد أن نعرف مؤثر كتلة ما، لا نحتاج إلى إعادة قراءة أرقامها مرة أخرى.
إذا كان لدينا بادئة عشرية مثبتة، فإن:
1. full_block(prefix, rem_digits) تمثل جميع الأعداد التي تبدأ بهذه البادئة ثم كل اللواحق الممكنة من الطول الباقي؛
2. partial_block(prefix, rem_digits, upper_suffix) تمثل البنية نفسها ولكن فقط حتى لاحقة علوية معينة.
وهذه المؤثرات تُخزَّن مع memoization لأن البنية نفسها تتكرر كثيرًا.
لمعالجة جميع الأعداد من \(1\) إلى \(N\)، لا يقوم الكود بتوليد السلسلة فعليًا. بل يركّب المؤثرات بالترتيب التالي:
1. جميع كتل أطوال الأرقام الكاملة الأصغر من طول \(N\)؛
2. داخل الطول الأعلى، جميع الكتل الكاملة التي يكون رقمها الأول أصغر من أول رقم في \(N\)؛
3. ثم كتلة جزئية أخيرة تصل بدقة إلى \(N\).
والنتيجة هي المؤثر
$$T_{[1..N]}.$$
وعند تطبيقه على الحالة الابتدائية \(0\)، نحصل على عدد ظهورات \(P\) في البادئة المنتهية \(123\dots N\).
نعرف
$$A(N)=\text{عدد ظهورات }P\text{ داخل }123\dots N.$$
ومن الواضح أن \(A(N)\) دالة غير تناقصية. لذلك يمكننا إجراء بحث ثنائي عن أصغر \(N\) يحقق
$$A(N)\ge n.$$
وهذا \(N\) هو بالضبط العدد الذي ينتهي داخله الظهور رقم \(n\) المطلوب.
بعد العثور على هذا \(N\) الأدنى، يحسب الكود أولًا حالة الأوتومات بعد قراءة \(123\dots(N-1)\). ثم يعيد مسح أرقام \(N\) فقط، ويعدّ التطابقات التي تظهر داخل هذا العدد الأخير.
إذا انتهى التطابق المطلوب عند الفهرس المحلي \(i\) داخل \(\text{str}(N)\)، وكان \(L=|P|\)، فإن موضع البداية العالمي يساوي
$$\text{digits\_before}(N-1)+i-L+2.$$
حيث
$$\text{digits\_before}(N)=\sum_{d=1}^{D-1}9\cdot 10^{d-1}\cdot d+\bigl(N-10^{D-1}+1\bigr)D,$$
و\(D\) هو عدد خانات \(N\).
تتحقق الشيفرة من عدة قيم معروفة:
$$f(1)=1,\qquad f(5)=81,\qquad f(12)=271,\qquad f(7780)=111111365.$$
كما أنها تقارن كل الحالات \(1\le n\le 30\) بمحاكاة brute-force مباشرة للتدفق.
تقوم الفئة PatternEngine بكل العمل الثقيل: تبني أوتومات KMP، وتعيد استخدام المؤثرات المتطابقة، وتخزن تراكيب المؤثرات، وتوفر:
1. range_operator(N) لبادئة \(123\dots N\)،
2. occ_and_state(N) لعدد التطابقات والحالة النهائية،
3. nth_occurrence_position(target) للبحث الثنائي مع المسح المحلي الأخير.
أما الحل الخارجي فيطبق هذا المحرك فقط على الأنماط \(3,9,27,\dots,3^{13}\) ثم يجمع المواضع الناتجة.
إذا كان \(L=|P|\)، فإن تركيب مؤثر واحد يكلف \(O(L)\). وبفضل memoization للكتل الكاملة والجزئية، يصبح تقييم \(A(N)\) أقرب بكثير إلى سلوك متعدد اللوغاريتم في \(N\) من كونه محاكاة خطية كاملة للتدفق. وهذا هو سبب إمكانية حل المسألة: البادئة الهائلة من \(C\) لا تُبنى صراحة أبدًا.