Problem Summary

Let \(T(n)\) be the number of length-\(n\) words over the seven distinct letters of “project” such that no contiguous block of length \(7\) is a permutation of those seven letters. We must compute \(T(10^{12}) \bmod 10^9\).

Because the alphabet has size \(7\), a forbidden block of length \(7\) is exactly a block whose seven letters are all different. So the problem is to count words in which every length-\(7\) window contains at least one repeated letter.

Mathematical Approach

Step 1: Only the newest 7-window can become invalid

Suppose we already have a valid word and append one more letter. Every old window of length \(7\) stays unchanged, so the only new constraint comes from the final window that ends at the appended position. Therefore, to know which letters may be appended, we only need information about the previous \(6\) characters.

This gives the problem a finite-state structure: the future depends only on the suffix of length \(6\), not on the entire earlier prefix.

Step 2: Replace concrete letters by a canonical equality pattern

The actual names of the letters do not matter; only the equality pattern inside the last six positions matters. Two suffixes are equivalent if equal positions match equal positions after consistently renaming letters. For example, the suffix \(a\,b\,a\,c\,b\,d\) becomes the canonical pattern

$$\left(0,1,0,2,1,3\right).$$

Reading left to right, the first new letter receives label \(0\), the next unseen letter receives label \(1\), and so on. These canonical patterns are exactly the set partitions of a 6-element set, so the number of states is the Bell number

$$B_6 = 203.$$

That is why the implementations work with a \(203\)-state transfer matrix instead of all \(7^6\) concrete suffixes.

Step 3: Weighted transitions between states

Take a canonical suffix pattern using \(k\) distinct labels. This means the current suffix contains exactly \(k\) distinct letters.

If we append a letter already present in the suffix, the new 7-window still contains at most \(k \le 6\) distinct letters, so it is always valid. There are exactly \(k\) such concrete choices. After dropping the oldest character and canonicalizing the new suffix again, each of those choices contributes weight \(1\) to some next state.

If we append a letter not present in the suffix, then the new 7-window contains \(k+1\) distinct letters. This is allowed only when \(k \le 5\). There are \(7-k\) such letters, and after canonical renaming they all lead to the same next pattern, so that transition receives weight \(7-k\).

When \(k=6\), no new letter may be appended, because that would create a window with all seven letters distinct, which is exactly the forbidden case.

Therefore, if \(v_m\) is the column vector whose entries count valid words of length \(m\) ending in each canonical state, then

$$v_{m+1}=A\,v_m \pmod{10^9},$$

where \(A\) is the \(203\times203\) weighted transition matrix.

Step 4: Initial vector at length 6

For length \(6\), every word is valid because no length-\(7\) window exists yet. If a canonical pattern uses \(k\) labels, then we must assign \(k\) distinct actual letters to those labels. The number of such assignments is the falling factorial

$$ (7)_k = 7\cdot 6\cdot 5\cdots(7-k+1)=\frac{7!}{(7-k)!}. $$

So the entry of \(v_6\) corresponding to a state with \(k\) labels is exactly \((7)_k\).

Step 5: Fast exponentiation in the word length

For \(n \le 6\), we simply have

$$T(n)=7^n.$$

For \(n \ge 6\), repeated application of the transition matrix gives

$$v_n=A^{n-6}v_6 \pmod{10^9},\qquad T(n)=\sum_{s}(v_n)_s \pmod{10^9}.$$

The huge exponent \(n-6\) is handled by exponentiation by squaring, so the dependence on \(n\) is only logarithmic.

Checkpoint Example

For \(n=7\), the only invalid words are the permutations of the seven distinct letters, one for each ordering of the alphabet. Hence

$$T(7)=7^7-7!=823543-5040=818503.$$

Applying one more transfer step yields

$$T(8)=5699281,$$

which matches the checkpoints verified by the implementations.

How the Code Works

The C++, Python, and Java implementations generate all 203 canonical suffix patterns, index them, and build the weighted transition matrix described above. They then create the length-\(6\) start vector using the falling-factorial count \((7)_k\), raise the matrix to the power \(n-6\) by repeated squaring modulo \(10^9\), and finally sum the entries of the resulting state vector.

Complexity Analysis

Let \(S=203\) be the number of states. State generation and transition construction are fixed-size preprocessing for this problem. The dominant cost is matrix exponentiation, which requires \(O(S^3\log n)\) time and \(O(S^2)\) memory. Since \(S\) is small and fixed, this easily handles \(n=10^{12}\).

Footnotes and References

  1. Problem page: https://projecteuler.net/problem=458
  2. Bell numbers and set partitions: Wikipedia — Bell number
  3. Falling factorials: Wikipedia — Falling and rising factorials
  4. Transfer-matrix method: Wikipedia — Transfer-matrix method
  5. Exponentiation by squaring: Wikipedia — Exponentiation by squaring

Problemzusammenfassung

Sei \(T(n)\) die Anzahl der Wörter der Länge \(n\) über den sieben verschiedenen Buchstaben von „project“, in denen kein zusammenhängender Block der Länge \(7\) eine Permutation dieser sieben Buchstaben ist. Gesucht ist \(T(10^{12}) \bmod 10^9\).

Da das Alphabet genau \(7\) Zeichen besitzt, ist ein verbotener 7er-Block genau dann vorhanden, wenn darin alle sieben Buchstaben verschieden sind. Jede zulässige 7er-Scheibe muss also mindestens einen Wiederholungsbuchstaben enthalten.

Mathematischer Ansatz

Schritt 1: Beim Anhängen zählt nur das neu entstehende 7er-Fenster

Hängen wir an ein bereits gültiges Wort einen weiteren Buchstaben an, dann bleiben alle alten 7er-Fenster unverändert. Neu geprüft werden muss nur das letzte Fenster, das an der neuen Position endet. Deshalb reicht es aus, die letzten \(6\) Zeichen zu kennen.

Damit entsteht ein endlicher Automat: Für die Zukunft ist nur der 6er-Suffix relevant, nicht der gesamte bisherige Präfix.

Schritt 2: Konkrete Buchstaben durch ein kanonisches Gleichheitsmuster ersetzen

Die konkreten Buchstaben sind symmetrisch; entscheidend ist nur, welche der letzten sechs Positionen gleich oder verschieden sind. Ein Suffix wie \(a\,b\,a\,c\,b\,d\) wird durch Umbenennung der zuerst gesehenen Buchstaben in die kanonische Form

$$\left(0,1,0,2,1,3\right)$$

überführt. Der erste neue Buchstabe bekommt Label \(0\), der nächste neue Buchstabe Label \(1\) usw.

Solche Muster entsprechen genau den Partitionen einer 6-elementigen Menge. Daher ist die Anzahl der Zustände die Bell-Zahl

$$B_6=203.$$

Genau deshalb arbeiten die Implementierungen mit nur \(203\) Zuständen statt mit allen \(7^6\) konkreten Suffixen.

Schritt 3: Gewichtete Übergänge

Betrachte einen Zustand, dessen Suffixmuster \(k\) verschiedene Labels verwendet. Dann enthält der aktuelle Suffix genau \(k\) verschiedene Buchstaben.

Wird ein bereits vorhandener Buchstabe angehängt, dann enthält das neue 7er-Fenster weiterhin höchstens \(k \le 6\) verschiedene Buchstaben und bleibt also gültig. Dafür gibt es genau \(k\) konkrete Möglichkeiten. Nach dem Entfernen des ältesten Zeichens und erneuter Kanonisierung trägt jede dieser Möglichkeiten Gewicht \(1\) zu einem Folgezustand bei.

Wird ein neuer, bislang im Suffix nicht vorkommender Buchstabe angehängt, dann enthält das neue 7er-Fenster \(k+1\) verschiedene Buchstaben. Das ist nur für \(k \le 5\) erlaubt. Es gibt dann \(7-k\) konkrete Wahlmöglichkeiten, und nach der kanonischen Umbenennung führen sie alle in dasselbe neue Muster; dieser Übergang erhält also Gewicht \(7-k\).

Für \(k=6\) ist kein neuer Buchstabe zulässig, denn dann würde das neue 7er-Fenster alle sieben Buchstaben genau einmal enthalten und wäre verboten.

Mit \(v_m\) als Spaltenvektor der Zustandsanzahlen für gültige Wörter der Länge \(m\) gilt daher

$$v_{m+1}=A\,v_m \pmod{10^9},$$

wobei \(A\) die gewichtete \(203\times203\)-Übergangsmatrix ist.

Schritt 4: Startvektor bei Länge 6

Für Länge \(6\) ist jedes Wort zulässig, da noch kein 7er-Fenster existiert. Verwendet ein kanonisches Muster \(k\) Labels, dann müssen diesen \(k\) Labels \(k\) verschiedene echte Buchstaben injektiv zugeordnet werden. Die Anzahl dieser Zuordnungen ist der fallende Faktor

$$ (7)_k = 7\cdot 6\cdot 5\cdots(7-k+1)=\frac{7!}{(7-k)!}. $$

Genau dieser Wert ist der entsprechende Eintrag von \(v_6\).

Schritt 5: Schnelles Potenzieren in der Wortlänge

Für \(n \le 6\) gilt direkt

$$T(n)=7^n.$$

Für \(n \ge 6\) folgt aus wiederholter Anwendung der Matrix

$$v_n=A^{n-6}v_6 \pmod{10^9},\qquad T(n)=\sum_s (v_n)_s \pmod{10^9}.$$

Die Potenz \(A^{n-6}\) wird mit Exponentiation by Squaring berechnet, daher wächst der Aufwand in \(n\) nur logarithmisch.

Kontrollbeispiel

Für \(n=7\) sind genau die \(7!\) Permutationen der sieben verschiedenen Buchstaben verboten. Also

$$T(7)=7^7-7!=823543-5040=818503.$$

Ein weiterer Matrixschritt liefert

$$T(8)=5699281,$$

genau den Kontrollwert der Implementierungen.

Wie der Code arbeitet

Die C++, Python- und Java-Implementierungen erzeugen zunächst alle 203 kanonischen Suffixmuster, nummerieren sie durch und bauen daraus die gewichtete Übergangsmatrix. Danach wird der Startvektor für Länge \(6\) mit den Werten \((7)_k\) gefüllt. Anschließend wird die Matrix modulo \(10^9\) per wiederholtem Quadrieren auf die Potenz \(n-6\) gebracht und die Summe aller Endzustände gebildet.

Komplexitätsanalyse

Mit \(S=203\) Zuständen ist die Zustands- und Übergangserzeugung für dieses Problem nur eine feste Vorverarbeitung. Der dominierende Teil ist die Matrixpotenzierung mit \(O(S^3\log n)\) Zeit und \(O(S^2)\) Speicher. Da \(S\) klein und konstant ist, bleibt auch \(n=10^{12}\) problemlos beherrschbar.

Fußnoten und Quellen

  1. Problemseite: https://projecteuler.net/problem=458
  2. Bell-Zahlen und Partitionen: Wikipedia — Bell-Zahl
  3. Fallende Fakultät: Wikipedia — Fallende und steigende Fakultät
  4. Transfer-Matrix-Methode: Wikipedia — Transfer-matrix method
  5. Exponentiation by Squaring: Wikipedia — Potenzieren durch Quadrieren

Problem Özeti

\(T(n)\), “project” kelimesindeki yedi farklı harf üzerinden oluşturulan uzunluğu \(n\) olan kelimelerin sayısı olsun. Koşul şudur: uzunluğu \(7\) olan hiçbir bitişik pencere bu yedi harfin bir permütasyonu olmamalıdır. Aranan değer \(T(10^{12}) \bmod 10^9\) olur.

Alfabenin boyutu tam olarak \(7\) olduğu için yasak bir 7'li pencere, ancak ve ancak içindeki yedi harfin hepsi farklıysa oluşur. Yani geçerli her 7'li pencerede en az bir tekrar bulunmalıdır.

Matematiksel Yaklaşım

Adım 1: Yeni harf eklenince sadece son 7'li pencere önemlidir

Geçerli bir kelimeye bir harf daha eklediğimizde eski 7'li pencerelerin hiçbiri değişmez. Dolayısıyla yalnızca yeni sona gelen 7'li pencerenin kontrol edilmesi gerekir. Bu da gelecekte hangi harflerin eklenebileceğini belirlemek için son \(6\) karakterin yeterli olduğu anlamına gelir.

Böylece problem sonlu durumlu bir yapıya dönüşür: geçmişin tamamı değil, sadece 6 uzunluklu son ek önemlidir.

Adım 2: Somut harfler yerine kanonik eşitlik deseni

Asıl harf kimlikleri önemli değildir; önemli olan son altı pozisyonda hangi yerlerin aynı harf olduğu bilgisidir. Örneğin \(a\,b\,a\,c\,b\,d\) son eki kanonik olarak

$$\left(0,1,0,2,1,3\right)$$

şeklinde yazılır. Soldan sağa giderken ilk yeni harf \(0\), sonraki yeni harf \(1\), daha sonra görülen yeni harf \(2\) diye etiketlenir.

Bu desenler, 6 elemanlı bir kümenin bölümlenmeleriyle bire bir aynıdır. Dolayısıyla durum sayısı Bell sayısıdır:

$$B_6=203.$$

Bu yüzden implementasyonlar tüm somut \(7^6\) son ekleri değil, yalnızca 203 kanonik durumu tutar.

Adım 3: Ağırlıklı geçişler

Bir durumun son ek deseni \(k\) farklı etiket kullansın. Bu, son altı karakter içinde tam olarak \(k\) farklı harf bulunduğu anlamına gelir.

Eğer son ekte zaten görülen bir harf eklenirse, yeni 7'li pencere en fazla \(k \le 6\) farklı harf içerir ve her zaman geçerlidir. Bunun tam \(k\) somut seçeneği vardır. En eski karakter atılıp yeni 6'lı son ek tekrar kanonikleştirildiğinde, bu seçeneklerin her biri bir sonraki duruma ağırlık \(1\) ekler.

Eğer son ekte bulunmayan yeni bir harf eklenirse, yeni 7'li pencere \(k+1\) farklı harf içerir. Bu yalnızca \(k \le 5\) iken izinlidir. Böyle harf sayısı \(7-k\) kadardır ve kanonik yeniden etiketlemeden sonra hepsi aynı yeni desene gider; dolayısıyla bu geçişin ağırlığı \(7-k\) olur.

\(k=6\) olduğunda yeni harf eklenemez; çünkü o zaman 7'li pencere yedi farklı harf içerir ve bu tam olarak yasak durumdur.

Sonuç olarak, \(v_m\) uzunluğu \(m\) olan geçerli kelimeleri durumlara göre sayan sütun vektörü ise

$$v_{m+1}=A\,v_m \pmod{10^9},$$

burada \(A\) ağırlıklı \(203\times203\) geçiş matrisidir.

Adım 4: Uzunluk 6 için başlangıç vektörü

Uzunluk \(6\) için her kelime geçerlidir; çünkü henüz uzunluğu \(7\) olan bir pencere yoktur. Eğer bir kanonik desen \(k\) etiket kullanıyorsa, bu etiketlere \(k\) farklı gerçek harf enjektif olarak atanmalıdır. Bunun sayısı düşen faktöriyeldir:

$$ (7)_k = 7\cdot 6\cdot 5\cdots(7-k+1)=\frac{7!}{(7-k)!}. $$

Dolayısıyla \(v_6\) içindeki ilgili giriş tam olarak \((7)_k\) olur.

Adım 5: Uzunluk boyunca hızlı üs alma

\(n \le 6\) için doğrudan

$$T(n)=7^n$$

elde edilir. \(n \ge 6\) için ise

$$v_n=A^{n-6}v_6 \pmod{10^9},\qquad T(n)=\sum_s (v_n)_s \pmod{10^9}.$$

Buradaki büyük üs, tekrar tekrar karesini alma yöntemiyle hesaplanır; böylece \(n\)'e bağımlılık logaritmik olur.

Kontrol Örneği

\(n=7\) için geçersiz olan tek kelimeler, yedi farklı harfin tüm permütasyonlarıdır. Bu yüzden

$$T(7)=7^7-7!=823543-5040=818503.$$

Bir geçiş adımı daha uygulandığında

$$T(8)=5699281$$

elde edilir; bu da implementasyonların doğrulama değerleriyle aynıdır.

Kodun Çalışma Mantığı

C++, Python ve Java implementasyonları önce 203 kanonik son ek desenini üretir, bunları indisler ve ağırlıklı geçiş matrisini kurar. Ardından uzunluk \(6\) için başlangıç vektörünü \((7)_k\) değerleriyle doldurur. Son aşamada matris \(10^9\) modunda ikili üs alma ile \(n-6\). kuvvete yükseltilir ve son durumların toplamı alınır.

Karmaşıklık Analizi

\(S=203\) durum sayısı olsun. Durumları üretmek ve geçişleri kurmak bu problem için sabit boyutlu bir ön işlemdir. Asıl maliyet matris üs almadadır: zaman karmaşıklığı \(O(S^3\log n)\), bellek karmaşıklığı \(O(S^2)\) olur. \(S\) küçük ve sabit olduğundan \(n=10^{12}\) rahatlıkla hesaplanabilir.

Dipnotlar ve Kaynakça

  1. Problem sayfası: https://projecteuler.net/problem=458
  2. Bell sayıları ve küme bölümlenmeleri: Wikipedia — Bell sayısı
  3. Düşen faktöriyel: Wikipedia — Falling and rising factorials
  4. Transfer matrisi yöntemi: Wikipedia — Transfer-matrix method
  5. Karesini alarak üs alma: Wikipedia — Exponentiation by squaring

Resumen del Problema

Sea \(T(n)\) el número de palabras de longitud \(n\) sobre las siete letras distintas de “project” tales que ningún bloque contiguo de longitud \(7\) sea una permutación de esas siete letras. Debemos calcular \(T(10^{12}) \bmod 10^9\).

Como el alfabeto tiene tamaño \(7\), un bloque prohibido de longitud \(7\) aparece exactamente cuando sus siete letras son todas distintas. En otras palabras, toda ventana válida de longitud \(7\) debe contener al menos una repetición.

Enfoque Matemático

Paso 1: Al extender la palabra solo importa la nueva ventana de longitud 7

Si una palabra ya es válida y añadimos una letra más, todas las ventanas antiguas de longitud \(7\) permanecen iguales. La única restricción nueva es la ventana final que termina en la posición recién añadida. Por eso basta conocer los \(6\) caracteres anteriores.

Esto convierte el problema en un sistema de estados finitos: el futuro depende solo del sufijo de longitud \(6\), no de todo el prefijo.

Paso 2: Sustituir letras concretas por un patrón canónico de igualdades

Las letras concretas son simétricas; lo importante es qué posiciones del sufijo son iguales entre sí. Por ejemplo, el sufijo \(a\,b\,a\,c\,b\,d\) se transforma en el patrón canónico

$$\left(0,1,0,2,1,3\right).$$

Al recorrer de izquierda a derecha, la primera letra nueva recibe la etiqueta \(0\), la siguiente letra no vista recibe la etiqueta \(1\), y así sucesivamente.

Estos patrones son exactamente las particiones de un conjunto de 6 posiciones. Por tanto, el número de estados es el número de Bell

$$B_6=203.$$

Así se reduce el problema desde los \(7^6\) sufijos concretos a solo \(203\) clases canónicas.

Paso 3: Transiciones con peso

Supongamos que un estado usa \(k\) etiquetas distintas. Eso significa que el sufijo actual contiene exactamente \(k\) letras diferentes.

Si añadimos una letra que ya aparece en el sufijo, la nueva ventana de longitud \(7\) sigue teniendo como máximo \(k \le 6\) letras distintas, así que siempre es válida. Hay exactamente \(k\) elecciones concretas de este tipo. Tras eliminar el carácter más antiguo y volver a canonizar el nuevo sufijo, cada una aporta peso \(1\) a algún estado siguiente.

Si añadimos una letra nueva, no presente todavía en el sufijo, entonces la nueva ventana tendrá \(k+1\) letras distintas. Eso solo está permitido cuando \(k \le 5\). Hay \(7-k\) letras posibles, y todas producen el mismo patrón canónico siguiente, así que esa transición lleva peso \(7-k\).

Cuando \(k=6\), no se puede añadir una letra nueva, porque eso crearía una ventana con las siete letras distintas, justamente la situación prohibida.

Si \(v_m\) es el vector columna que cuenta las palabras válidas de longitud \(m\) según su estado final, obtenemos

$$v_{m+1}=A\,v_m \pmod{10^9},$$

donde \(A\) es la matriz de transición ponderada de tamaño \(203\times203\).

Paso 4: Vector inicial en longitud 6

Para longitud \(6\), toda palabra es válida porque todavía no existe ninguna ventana de longitud \(7\). Si un patrón canónico usa \(k\) etiquetas, debemos asignarles \(k\) letras reales distintas de forma inyectiva. El número de asignaciones es el factorial decreciente

$$ (7)_k = 7\cdot 6\cdot 5\cdots(7-k+1)=\frac{7!}{(7-k)!}. $$

Ese es exactamente el valor de la componente correspondiente de \(v_6\).

Paso 5: Exponenciación rápida en la longitud

Para \(n \le 6\), simplemente

$$T(n)=7^n.$$

Para \(n \ge 6\),

$$v_n=A^{n-6}v_6 \pmod{10^9},\qquad T(n)=\sum_s (v_n)_s \pmod{10^9}.$$

La potencia grande \(A^{n-6}\) se calcula por exponenciación binaria, de modo que la dependencia en \(n\) es logarítmica.

Ejemplo de comprobación

Para \(n=7\), las únicas palabras inválidas son las \(7!\) permutaciones de las siete letras distintas. Por tanto,

$$T(7)=7^7-7!=823543-5040=818503.$$

Aplicando un paso más de la matriz se obtiene

$$T(8)=5699281,$$

que coincide con los valores de comprobación de las implementaciones.

Cómo Funciona el Código

Las implementaciones en C++, Python y Java generan los 203 patrones canónicos del sufijo, los indexan y construyen la matriz de transición ponderada. Luego inicializan el vector de longitud \(6\) con los conteos \((7)_k\), elevan la matriz a la potencia \(n-6\) mediante cuadrados sucesivos módulo \(10^9\) y finalmente suman todas las entradas del vector resultante.

Análisis de Complejidad

Sea \(S=203\) el número de estados. Generar los estados y las transiciones es un preprocesamiento de tamaño fijo para este problema. El coste dominante es la exponenciación matricial: \(O(S^3\log n)\) en tiempo y \(O(S^2)\) en memoria. Como \(S\) es pequeño y constante, el método maneja sin dificultad \(n=10^{12}\).

Referencias

  1. Página del problema: https://projecteuler.net/problem=458
  2. Números de Bell y particiones: Wikipedia — Número de Bell
  3. Factorial decreciente: Wikipedia — Falling and rising factorials
  4. Método de la matriz de transferencia: Wikipedia — Transfer-matrix method
  5. Exponenciación por cuadrados: Wikipedia — Exponenciación rápida

问题概述

设 \(T(n)\) 表示长度为 \(n\) 的字符串个数,字母表是 “project” 的 7 个不同字母,并且要求任意长度为 \(7\) 的连续窗口都不是这 7 个字母的一个排列。目标是计算 \(T(10^{12}) \bmod 10^9\)。

因为字母表大小正好是 \(7\),所以一个长度为 \(7\) 的窗口被禁止,当且仅当其中 7 个字符两两不同。换句话说,每个合法的 7 长窗口都必须至少出现一次重复字母。

数学方法

步骤 1:扩展字符串时,只需要检查最新形成的 7 长窗口

如果当前字符串已经合法,再追加一个字符,那么之前所有长度为 \(7\) 的窗口都不会变化。新的约束只来自最后那个以新字符结尾的窗口。因此,要判断下一步能加什么字符,只需要知道前面的最后 \(6\) 个字符。

这说明问题可以建成有限状态系统:未来只依赖长度为 \(6\) 的后缀,而不依赖更早的前缀。

步骤 2:用规范化的“相等关系模式”代替具体字母

具体是哪几个字母并不重要,重要的是最后 6 个位置之间哪些相等、哪些不同。例如后缀 \(a\,b\,a\,c\,b\,d\) 可以规范化为

$$\left(0,1,0,2,1,3\right).$$

从左到右扫描时,第一个新字母记为 \(0\),下一个未出现过的新字母记为 \(1\),依此类推。

这样的模式恰好对应于 6 个位置的集合划分,因此状态总数就是 Bell 数

$$B_6=203.$$

这就是为什么实现中只需要处理 203 个规范状态,而不是全部 \(7^6\) 个具体后缀。

步骤 3:构造带权转移

设某个状态的后缀模式使用了 \(k\) 个不同标签,这意味着当前后缀中正好有 \(k\) 个不同字母。

如果追加一个已经出现在后缀中的字母,那么新的 7 长窗口仍然至多只有 \(k \le 6\) 个不同字母,所以一定合法。这样的具体选择恰好有 \(k\) 个。删去最旧字符并重新规范化后,每一种选择都会向某个后继状态贡献权重 \(1\)。

如果追加一个当前后缀中没有出现过的新字母,那么新的 7 长窗口将有 \(k+1\) 个不同字母。这只有在 \(k \le 5\) 时才允许。可选新字母共有 \(7-k\) 个,而且规范化之后都会落到同一个后继模式,因此该转移的权重是 \(7-k\)。

当 \(k=6\) 时,不能再追加新字母,因为那样就会形成包含 7 个不同字母的窗口,也就是被禁止的情形。

于是如果 \(v_m\) 是按最终状态统计的长度为 \(m\) 的合法字符串计数向量,就有

$$v_{m+1}=A\,v_m \pmod{10^9},$$

其中 \(A\) 是一个 \(203\times203\) 的带权转移矩阵。

步骤 4:长度 6 的初始向量

当长度为 \(6\) 时,还不存在任何长度为 \(7\) 的窗口,所以所有字符串都合法。若某个规范模式使用了 \(k\) 个标签,就必须把 \(k\) 个不同的真实字母一一分配给这些标签。分配方式数是下降阶乘

$$ (7)_k = 7\cdot 6\cdot 5\cdots(7-k+1)=\frac{7!}{(7-k)!}. $$

因此 \(v_6\) 中对应状态的初值正是 \((7)_k\)。

步骤 5:对长度做快速幂

对于 \(n \le 6\),直接有

$$T(n)=7^n.$$

对于 \(n \ge 6\),

$$v_n=A^{n-6}v_6 \pmod{10^9},\qquad T(n)=\sum_s (v_n)_s \pmod{10^9}.$$

由于使用二分幂,矩阵指数 \(n-6\) 的处理只需要对数级步骤。

校验示例

当 \(n=7\) 时,唯一非法的字符串就是 7 个不同字母的全部 \(7!\) 个排列,因此

$$T(7)=7^7-7!=823543-5040=818503.$$

再进行一次状态转移,就得到

$$T(8)=5699281,$$

与实现中使用的校验值一致。

代码如何工作

C++、Python 和 Java 实现都会先生成全部 203 个规范后缀模式,为它们建立编号,再构造上面的带权转移矩阵。随后用下降阶乘 \((7)_k\) 填充长度 \(6\) 的初始向量,并在模 \(10^9\) 下通过重复平方计算 \(A^{n-6}\),最后把结果向量的所有分量求和。

复杂度分析

记状态数为 \(S=203\)。状态生成和转移构造在本题中只是固定规模的预处理。主导成本来自矩阵快速幂,时间复杂度为 \(O(S^3\log n)\),空间复杂度为 \(O(S^2)\)。由于 \(S\) 很小且固定,即使 \(n=10^{12}\) 也完全可行。

参考资料

  1. 题目页面: https://projecteuler.net/problem=458
  2. Bell 数与集合划分: Wikipedia — 贝尔数
  3. 下降阶乘: Wikipedia — Falling and rising factorials
  4. 传递矩阵方法: Wikipedia — Transfer-matrix method
  5. 平方求幂: Wikipedia — 快速幂

Краткое описание

Пусть \(T(n)\) обозначает число слов длины \(n\) над семью различными буквами слова “project”, в которых ни один непрерывный блок длины \(7\) не является перестановкой этих семи букв. Требуется найти \(T(10^{12}) \bmod 10^9\).

Так как мощность алфавита равна \(7\), запрещённый блок длины \(7\) возникает тогда и только тогда, когда все семь символов в нём различны. Значит, в каждом допустимом окне длины \(7\) должна быть хотя бы одна повторяющаяся буква.

Математический подход

Шаг 1: При добавлении новой буквы нужно проверять только последнее окно

Если слово уже допустимо и мы добавляем ещё один символ, все старые окна длины \(7\) остаются теми же. Новым становится только последнее окно, заканчивающееся на добавленной позиции. Следовательно, чтобы понять, какие продолжения разрешены, достаточно знать предыдущие \(6\) символов.

Это превращает задачу в конечный автомат: дальнейшее поведение зависит только от суффикса длины \(6\), а не от всего префикса.

Шаг 2: Канонический шаблон равенств вместо конкретных букв

Имена букв несущественны; важно только, какие позиции в последних шести символах совпадают. Например, суффикс \(a\,b\,a\,c\,b\,d\) после канонической перенумерации превращается в

$$\left(0,1,0,2,1,3\right).$$

При просмотре слева направо первая новая буква получает метку \(0\), следующая новая буква метку \(1\) и так далее.

Такие шаблоны в точности соответствуют разбиениям множества из 6 позиций, поэтому число состояний равно числу Белла

$$B_6=203.$$

Именно поэтому реализации работают с \(203\) состояниями, а не со всеми \(7^6\) конкретными суффиксами.

Шаг 3: Взвешенные переходы

Пусть текущий шаблон суффикса использует \(k\) различных меток. Это означает, что в текущем суффиксе ровно \(k\) различных букв.

Если дописать букву, которая уже присутствует в суффиксе, то новое окно длины \(7\) по-прежнему содержит не более \(k \le 6\) различных букв и потому всегда допустимо. Таких конкретных продолжений ровно \(k\). После удаления самого старого символа и новой канонизации каждое такое продолжение даёт вклад веса \(1\) в некоторый следующий шаблон.

Если дописать новую букву, отсутствующую в суффиксе, то новое окно будет содержать \(k+1\) различных букв. Это разрешено только при \(k \le 5\). Таких букв \(7-k\), и после канонической перенумерации все они приводят к одному и тому же следующему шаблону, поэтому вес такого перехода равен \(7-k\).

При \(k=6\) новая буква запрещена, потому что тогда возникнет окно из семи различных букв, то есть именно запрещённый случай.

Если \(v_m\) обозначает столбец количеств допустимых слов длины \(m\), разбитых по конечному состоянию, то

$$v_{m+1}=A\,v_m \pmod{10^9},$$

где \(A\) есть взвешенная матрица переходов размера \(203\times203\).

Шаг 4: Начальный вектор для длины 6

При длине \(6\) любое слово допустимо, поскольку ещё не существует окна длины \(7\). Если канонический шаблон использует \(k\) меток, этим меткам нужно инъективно сопоставить \(k\) различных реальных букв. Число таких сопоставлений равно падающему факториалу

$$ (7)_k = 7\cdot 6\cdot 5\cdots(7-k+1)=\frac{7!}{(7-k)!}. $$

Следовательно, соответствующая компонента вектора \(v_6\) равна \((7)_k\).

Шаг 5: Быстрое возведение матрицы в степень

Для \(n \le 6\) сразу имеем

$$T(n)=7^n.$$

Для \(n \ge 6\) получаем

$$v_n=A^{n-6}v_6 \pmod{10^9},\qquad T(n)=\sum_s (v_n)_s \pmod{10^9}.$$

Степень \(A^{n-6}\) вычисляется методом двоичного возведения в степень, поэтому зависимость от \(n\) логарифмическая.

Проверочный пример

При \(n=7\) недопустимы только \(7!\) перестановок семи различных букв. Поэтому

$$T(7)=7^7-7!=823543-5040=818503.$$

Ещё один шаг матрицы даёт

$$T(8)=5699281,$$

что совпадает с контрольными значениями в реализациях.

Как работает код

Реализации на C++, Python и Java сначала перечисляют все 203 канонических шаблона суффикса, присваивают им индексы и строят взвешенную матрицу переходов. Затем они формируют начальный вектор длины \(6\) с использованием значений \((7)_k\), возводят матрицу в степень \(n-6\) методом повторного возведения в квадрат по модулю \(10^9\) и суммируют все компоненты итогового вектора.

Анализ сложности

Пусть \(S=203\) — число состояний. Построение состояний и переходов здесь является лишь фиксированным предварительным этапом. Основная стоимость приходится на возведение матрицы в степень: \(O(S^3\log n)\) по времени и \(O(S^2)\) по памяти. Поскольку \(S\) мало и постоянно, метод легко справляется даже с \(n=10^{12}\).

Ссылки и литература

  1. Страница задачи: https://projecteuler.net/problem=458
  2. Числа Белла и разбиения: Wikipedia — Числа Белла
  3. Падающий факториал: Wikipedia — Falling and rising factorials
  4. Метод передаточных матриц: Wikipedia — Transfer-matrix method
  5. Быстрое возведение в степень: Wikipedia — Быстрое возведение в степень

ملخص المسألة

ليكن \(T(n)\) عدد الكلمات ذات الطول \(n\) المبنية من الحروف السبعة المختلفة في كلمة “project”، بشرط ألا تكون أي قطعة متجاورة بطول \(7\) ترتيبًا كاملًا لهذه الحروف السبعة. المطلوب هو حساب \(T(10^{12}) \bmod 10^9\).

وبما أن حجم الأبجدية يساوي \(7\) بالضبط، فإن النافذة الممنوعة بطول \(7\) تظهر إذا وفقط إذا كانت حروفها السبعة كلها مختلفة. أي إن كل نافذة صالحة بطول \(7\) يجب أن تحتوي على تكرار واحد على الأقل.

المنهج الرياضي

الخطوة 1: عند إضافة حرف جديد لا يلزم فحص إلا النافذة الأخيرة

إذا كانت الكلمة الحالية صالحة ثم أضفنا حرفًا جديدًا، فإن جميع النوافذ القديمة بطول \(7\) تبقى كما هي. القيد الجديد الوحيد يأتي من النافذة الأخيرة التي تنتهي عند الحرف المضاف. لذلك يكفي أن نعرف آخر \(6\) أحرف فقط لكي نحدد الامتدادات المسموح بها.

وهذا يحول المسألة إلى آلة حالات منتهية: المستقبل يعتمد فقط على اللاحقة ذات الطول \(6\)، لا على كل البادئة السابقة.

الخطوة 2: استبدال الحروف الفعلية بنمط قانوني لعلاقات التساوي

هوية الحروف نفسها ليست مهمة؛ المهم هو أي المواضع في آخر ستة أحرف متساوية وأيها مختلفة. فمثلًا اللاحقة \(a\,b\,a\,c\,b\,d\) تتحول إلى النمط القانوني

$$\left(0,1,0,2,1,3\right).$$

وأثناء المسح من اليسار إلى اليمين نعطي أول حرف جديد الرمز \(0\)، ثم الحرف الجديد التالي الرمز \(1\)، وهكذا.

هذه الأنماط تطابق تمامًا تقسيمات مجموعة مكونة من 6 مواضع، ولذلك يكون عدد الحالات هو عدد بيل

$$B_6=203.$$

ولهذا السبب تعمل الحلول على 203 حالة فقط بدلًا من جميع اللواحق الممكنة وعددها \(7^6\).

الخطوة 3: الانتقالات ذات الأوزان

افترض أن نمط اللاحقة الحالي يستخدم \(k\) رموزًا مختلفة، وهذا يعني أن آخر ستة أحرف تحتوي بالضبط على \(k\) حروف مختلفة.

إذا أضفنا حرفًا موجودًا أصلًا في اللاحقة، فإن النافذة الجديدة بطول \(7\) ستظل تحتوي على عدد لا يتجاوز \(k \le 6\) من الحروف المختلفة، ولذلك تكون صالحة دائمًا. وعدد هذه الاختيارات الملموسة هو \(k\). وبعد حذف أقدم حرف وإعادة الصياغة القانونية لللاحقة الجديدة، يضيف كل اختيار وزنًا مقداره \(1\) إلى حالة لاحقة ما.

أما إذا أضفنا حرفًا جديدًا غير موجود في اللاحقة، فستحتوي النافذة الجديدة على \(k+1\) حروف مختلفة. وهذا مسموح فقط عندما \(k \le 5\). وعدد هذه الحروف هو \(7-k\)، وبعد إعادة التسمية القانونية تؤدي كلها إلى النمط التالي نفسه، ولذلك يكون وزن هذا الانتقال هو \(7-k\).

عندما \(k=6\) لا يجوز إضافة حرف جديد، لأن ذلك سيكوّن نافذة تحتوي على الحروف السبعة المختلفة كلها، وهي الحالة الممنوعة تمامًا.

إذا كان \(v_m\) متجهًا عموديًا تعد مكوناته الكلمات الصالحة ذات الطول \(m\) بحسب الحالة النهائية، فإننا نحصل على

$$v_{m+1}=A\,v_m \pmod{10^9},$$

حيث \(A\) هي مصفوفة الانتقال الموزونة ذات الحجم \(203\times203\).

الخطوة 4: متجه البداية عند الطول 6

عند الطول \(6\) تكون كل كلمة صالحة، لأنه لا توجد بعد أي نافذة بطول \(7\). وإذا كان النمط القانوني يستخدم \(k\) رموز، فعلينا إسناد \(k\) حروف حقيقية مختلفة إلى هذه الرموز إسنادًا حقنيًا. وعدد هذه الإسنادات هو المضروب الساقط

$$ (7)_k = 7\cdot 6\cdot 5\cdots(7-k+1)=\frac{7!}{(7-k)!}. $$

ومن ثم تكون القيمة الموافقة في \(v_6\) مساوية تمامًا لـ \((7)_k\).

الخطوة 5: رفع المصفوفة بسرعة

إذا كان \(n \le 6\) فلدينا مباشرة

$$T(n)=7^n.$$

أما إذا كان \(n \ge 6\) فإنا نحصل على

$$v_n=A^{n-6}v_6 \pmod{10^9},\qquad T(n)=\sum_s (v_n)_s \pmod{10^9}.$$

ويُحسب الأس الكبير \(n-6\) بطريقة التربيع المتكرر، ولذلك تصبح العلاقة مع \(n\) لوغاريتمية فقط.

مثال تحقق

عند \(n=7\)، الكلمات غير الصالحة الوحيدة هي جميع التبديلات الممكنة للحروف السبعة المختلفة، وعددها \(7!\). لذلك

$$T(7)=7^7-7!=823543-5040=818503.$$

وبإجراء خطوة انتقال واحدة إضافية نحصل على

$$T(8)=5699281,$$

وهو نفس مقدار التحقق الذي تستعمله الحلول.

كيف تعمل الشيفرة

تقوم تطبيقات C++ وPython وJava أولًا بتوليد جميع أنماط اللواحق القانونية وعددها 203، ثم تعطي كل حالة فهرسًا وتبني مصفوفة الانتقال الموزونة. بعد ذلك تُنشئ متجه البداية للطول \(6\) باستخدام القيم \((7)_k\)، ثم ترفع المصفوفة إلى القوة \(n-6\) بطريقة التربيع المتكرر تحت modulo \(10^9\)، وفي النهاية تجمع جميع مكونات متجه الحالة النهائي.

تحليل التعقيد

ليكن \(S=203\) عدد الحالات. توليد الحالات وبناء الانتقالات هنا مجرد معالجة تمهيدية ثابتة الحجم لهذه المسألة. الكلفة الأساسية تأتي من رفع المصفوفة: الزمن \(O(S^3\log n)\) والذاكرة \(O(S^2)\). وبما أن \(S\) صغير وثابت، فإن حتى \(n=10^{12}\) يبقى ممكنًا عمليًا.

مراجع

  1. صفحة المسألة: https://projecteuler.net/problem=458
  2. أعداد بيل وتقسيمات المجموعات: Wikipedia — عدد بل
  3. المضروب الساقط: Wikipedia — Falling and rising factorials
  4. طريقة مصفوفة الانتقال: Wikipedia — Transfer-matrix method
  5. التربيع المتكرر: Wikipedia — Exponentiation by squaring