This commit is contained in:
scms 2024-03-12 22:14:11 -07:00
parent 310b69757f
commit 035cd1b1fe
2 changed files with 134 additions and 0 deletions

107
p98.lisp Normal file
View File

@ -0,0 +1,107 @@
(defparameter *a-code* (char-code #\A))
(defparameter *words*
(as-> (uiop:read-file-line "0098_words.txt") @
(uiop:split-string @ :separator '(#\,))
(mapcar (lambda (q) (subseq q 1 (1- (length q)))) @)))
;;; Digit utilities
(defun number->digits (n)
"Returns a list of decimal digits in N (note: not in order!)"
(-> (loop for m = n then (floor m 10)
for d = (mod m 10)
while (> m 0)
collect d)
(nreverse)))
(defun digits->number (digits)
"Returns the decimal number represented by DIGITS"
(loop with result = 0
for digit in digits
do (setf result (+ (* 10 result)
digit))
finally (return result)))
;;; Word and letter anagram utilities
(defun count-letters (word)
"Counts the (upper case) letters in string WORD"
(let ((counts (make-array 26 :initial-element 0)))
(loop for character across word
do (incf (aref counts (- (char-code character) *a-code*))))
(sort (loop for i upfrom 0
for count across counts
unless (zerop count)
collect (cons (code-char (+ i *a-code*)) count))
#'char<
:key #'car)))
(defun count-digits (n)
"Counts the digits in N"
(let ((counts (make-array 10 :initial-element 0)))
(loop for d in (digits n)
do (incf (aref counts d)))
(sort (loop for i upfrom 0
for count across counts
unless (zerop count)
collect (cons i count))
#'<
:key #'car)))
(defun find-anagrams (items get-count)
"Find anagrams in COUNTS-LIST, a list of (CONS WORD LETTER-COUNTS) (or analogous) for each item"
(let ((groups (make-hash-table :test 'equal)))
(loop for item in items
for counts = (funcall get-count item)
for record = (gethash counts groups (cons nil 0))
do (incf (cdr record))
(push item (car record))
(setf (gethash counts groups) record))
(loop for counts being the hash-keys in groups using (hash-value record)
when (> (cdr record) 1)
collect (car record))))
(defun squarep (n)
(let ((root (floor (sqrt n))))
(= (* root root) n)))
(defun for-each-substitution (function letters)
(for-each-combination
(lambda (digits)
(for-each-permutation
(lambda (p)
(funcall function
(loop for digit in p
for letter in letters
collect (cons letter digit))))
digits))
'(0 1 2 3 4 5 6 7 8 9)
(length letters)))
(defun substitute-all (word substitutions)
(loop for letter across word
collect (cdr (assoc letter substitutions))))
(defun find-anagram-square (anagram)
"Finds the largest number (if any) that is part of a square anagram based off letter-to-digit conversion from a word to number, of a word anagram"
(let ((matches nil)
(letters (loop with letters = nil
for character across (first anagram)
do (pushnew character letters)
finally (return letters))))
;; Brute force
(for-each-substitution
(lambda (substitutions)
(let* ((digits (mapcar (lambda (w) (substitute-all w substitutions)) anagram))
(numbers (loop for d in digits unless (= 0 (first d)) collect (digits->number d)))
(squares (loop for number in numbers when (squarep number) collect number)))
(when (>= (length squares) 2)
(setf matches (nconc matches squares)))))
letters)
(when matches
(reduce #'max matches))))
(defun anagramic-squares ()
(let ((word-anagrams (find-anagrams *words* #'count-letters)))
(loop for anagram in word-anagrams
for number = (find-anagram-square anagram)
maximize (or number 0))))

View File

@ -19,6 +19,12 @@
do (setf result (append form (list result)))
finally (return result)))
(defmacro as-> (start symbol &rest rest)
(loop with result = start
for form in rest
do (setf result (substitute result symbol form))
finally (return result)))
(defmacro multf (place multiplier)
`(setf ,place (* ,place ,multiplier)))
@ -64,3 +70,24 @@
(defun divisiblep (n divisor)
"Returns non-NIL when N is divisible by DIVISOR"
(= 0 (mod n divisor)))
(defun remove-index (index sequence)
"Returns a new list that is a copy of SEQUENCE with the item at INDEX removed"
(loop for i upfrom 0
for item in sequence
unless (= i index) collect item))
(defun for-each-permutation (f a &optional tail)
"Calls F on each permutation of sequence A, optionally consed onto TAIL"
(cond ((null a) (funcall f tail))
(t (loop with length = (length a)
for i from 0 below length
for remaining = (remove-index i a)
do (for-each-permutation f remaining (cons (elt a i) tail))))))
(defun for-each-combination (function list n)
(labels ((recurse (list n set)
(cond ((zerop n) (funcall function set))
(t (loop for (item . rest) on list do
(recurse rest (1- n) (cons item set)))))))
(recurse list n nil)))