view julia/strings.jl @ 25:203bb5bbeea5

Strings: Alien dictionary example (with some DP)
author Lewin Bormann <lbo@spheniscida.de>
date Fri, 03 Mar 2023 12:49:11 +0100
parents c1db337c15be
children
line wrap: on
line source


# "(())()" -> good;
# "(()" -> bad
function goodparens(s::String)::Bool
    nesting = 0
    for c in s
        if c == '('
            nesting += 1
        elseif c == ')'
            nesting -= 1
        end
        if nesting < 0
            return false
        end
    end
    if nesting != 0
        false
    else
        true
    end
end

# "[()]{}" -> good; "[(])" -> bad
function goodmultiparens(s::String)::Bool
    stack::Vector{Char} = []
    for c in s
        if c in ['(', '[', '{']
            push!(stack, c)
        elseif c in [')', ']', '}']
            o = pop!(stack);
            if (c == ')' && o == '(') || (c==']' && o == '[') || (c=='}' && o == '{')
                continue
            else
                return false
            end
        end
    end
    length(stack) == 0
end

function ad_extract_prefixes(strings::AbstractVector{String}, l=1)
    prefixes = Tuple{Int, String}[]
    for (i, s) = enumerate(strings)
        if length(s) < l || (!isempty(prefixes) && (@view s[begin:l]) == prefixes[end][2])
            continue
        end
        push!(prefixes, (i, s[begin:l]))
    end
    prefixes
end

# Without tracking
function ad_longest_path(adj::Matrix{Bool}, start)::Int
    neighbors = findall(@view adj[start,:])
    if isempty(neighbors)
        1
    else
        1 + maximum(ad_longest_path(adj, n) for n = neighbors)
    end
end

function ad_longest_path_tracking(adj::Matrix{Bool}, start, L=0, Lmax=[0], byL=zeros(Int, size(adj, 1)))
    neighbors = findall(@view adj[start,:])
    if isempty(neighbors)
        if L+1 > Lmax[1]
            byL[L+1] = start
            Lmax[1] = L+1
        end
        L+1
    else
        longest = 0
        for n = neighbors
            L_ = ad_longest_path_tracking(adj, n, L+1, Lmax, byL)
            longest = max(longest, L_)
            if L_ == Lmax[1]
                byL[L+1] = start
            end
        end
        longest
    end
end

"""
https://interviewing.io/questions/alien-dictionary
"""
function alien_dictionary(strings::AbstractVector{String})::String
    adj = zeros(Bool, 26, 26)
    char_to_num(c) = 1 + c - 'a'
    num_to_char(n) = 'a' - 1 + n

    max_str_len = maximum(length.(strings))

    for prefix_len = 0:max_str_len
        prefixes = ad_extract_prefixes(strings, prefix_len)

        for (start, pref) = prefixes
            # Loop through strings for each prefix
            
            for entry = start+1:length(strings)
                prev, next = strings[entry-1], strings[entry]
                if !startswith(next, pref)
                    break
                end
                if length(prev) == prefix_len || length(next) == prefix_len
                    continue
                end
                prev_c, next_c = prev[prefix_len+1], next[prefix_len+1]
                if prev_c == next_c
                    continue
                end
                adj[char_to_num(prev_c), char_to_num(next_c)] = true
            end
        end
    end
    start = char_to_num(strings[begin][begin])
    byL = zeros(Int, 26)
    L = ad_longest_path_tracking(adj, start, 0, [0], byL)
    String(num_to_char.(byL[1:L]))
end