LeetCode 127. Word Ladder in F#

URL

leetcode.com/problems/word-ladder

Code

github.com/syohex/dotnet-study/blob/master/..

#r "nuget: Fsharpx.Collections"

open FSharpx.Collections

let strToCharArray (s: string) : char array = s |> Seq.toArray

let oneDistancePattern (s: string) : char [] list =
    let rec oneDistancePattern' (s: char []) i acc =
        if i >= (Array.length s) then
            acc |> List.rev
        else
            let pattern = Array.copy s
            pattern.[i] <- '?'
            oneDistancePattern' s (i + 1) (pattern :: acc)

    oneDistancePattern' (s |> strToCharArray) 0 []

let makeGraph (wordList: string list) : Map<char [], Set<string>> =
    let rec makeGraph' wordList m =
        match wordList with
        | [] -> m
        | head :: tail ->
            let patterns = oneDistancePattern head

            let newGraph =
                patterns
                |> List.fold
                    (fun m p ->
                        match Map.tryFind p m with
                        | None -> Map.add p (Set.empty |> Set.add head) m
                        | Some s -> Map.add p (Set.add head s) m)
                    m

            makeGraph' tail newGraph

    makeGraph' wordList Map.empty

let ladderLength (beginWord: string) (endWord: string) (wordList: string list) : int =
    let rec ladderLength' endWord graph (queue: IPriorityQueue<(int * string)>) (visited: Set<string>) : int =
        match PriorityQueue.tryPop queue with
        | None -> 0
        | Some ((count, next), restQ) ->
            if next = endWord then
                count
            else
                let patterns = oneDistancePattern next

                let nexts =
                    patterns
                    |> List.choose (fun p -> Map.tryFind p graph)
                    |> List.fold (fun acc s -> s |> Set.fold (fun a cand -> Set.add cand a) acc) Set.empty

                let notVisited =
                    nexts
                    |> Set.filter (fun n -> Set.contains n visited |> not)

                let updatedQ =
                    notVisited
                    |> Set.fold (fun q next -> PriorityQueue.insert (count + 1, next) q) restQ

                let updatedVisited =
                    notVisited
                    |> Set.fold (fun s next -> Set.add next s) visited

                ladderLength' endWord graph updatedQ updatedVisited

    let graph = makeGraph wordList

    let queue =
        PriorityQueue.empty false
        |> PriorityQueue.insert (1, beginWord)

    let visited = Set.empty |> Set.add beginWord

    ladderLength' endWord graph queue visited
`