Хитрость заключается в том, чтобы при переходе по списку сохранять четыре вещи:
- текущую цепочку (примечание: вы можете построить их в обратном направлении, поскольку все элементы одинаковы!);
- как долго это будет;
- самая длинная цепь, которую вы видели;
- как долго это было.
Тогда вы принимаете решения на основе того, является ли элемент, на который вы смотрите, тем же самым, что и первый элемент в текущей цепочке (все еще строит ту же цепочку) или нет (начиная новую цепочку), и, если вы все еще строите ту же цепочку, эта цепочка теперь самая длинная.
Например:
(define (longest-chain l)
(let lc-loop ([tail l]
[current-length 0]
[current-chain '()]
[longest-length 0]
[longest-chain '()])
(cond [(null? tail)
;; we're done: return the longest chain
longest-chain]
[(and (not (null? current-chain))
(eqv? (first tail) (first current-chain)))
;; building on a current chain
(let ([chain (cons (first tail) current-chain)]
[chain-length (+ 1 current-length)])
(if (> chain-length longest-length)
;; the current chain is now the longest
(lc-loop (rest tail)
chain-length chain
chain-length chain)
;; it's not the longest yet
(lc-loop (rest tail)
chain-length chain
longest-length longest-chain)))]
[else
;; starting a new chain
(lc-loop (rest tail)
1 (list (first tail))
longest-length longest-chain)])))
Для дополнительных точек: если имеется более одной самой длинной цепочки, какую из них возвращает эта функция? Как ты мог изменить это, чтобы он сделал другой выбор? Или даже выбор random !
Обратите внимание, что в приведенной выше версии функции используется с именем let
, который является стандартной конструкцией в схеме. Если вы не хотите использовать это, вы можете просто превратить его в явную функцию:
(define (longest-chain l)
(define (lc-loop tail
current-length current-chain
longest-length longest-chain)
(cond [(null? tail)
;; we're done: return the longest chain
longest-chain]
[(and (not (null? current-chain))
(eqv? (first tail) (first current-chain)))
;; building on a current chain
(let ([chain (cons (first tail) current-chain)]
[chain-length (+ 1 current-length)])
(if (> chain-length longest-length)
;; the current chain is now the longest
(lc-loop (rest tail)
chain-length chain
chain-length chain)
;; it's not the longest yet
(lc-loop (rest tail)
chain-length chain
longest-length longest-chain)))]
[else
;; starting a new chain
(lc-loop (rest tail)
1 (list (first tail))
longest-length longest-chain)]))
(lc-loop l 0 '() 0 '()))
Это полностью эквивалентно вышеприведенной версии. Если вас не устраивают внутренние define
s, тогда вы можете превратить lc-loop
в определение верхнего уровня.