changeset 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 d660fc20d950
children 015fe10a59cd
files julia/strings.jl
diffstat 1 files changed, 78 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/julia/strings.jl	Fri Mar 03 00:23:39 2023 +0100
+++ b/julia/strings.jl	Fri Mar 03 12:49:11 2023 +0100
@@ -38,4 +38,82 @@
     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