diff options
| -rw-r--r-- | challenge-096/paulo-custodio/basic/ch-1.bas | 57 | ||||
| -rw-r--r-- | challenge-096/paulo-custodio/basic/ch-2.bas | 138 | ||||
| -rw-r--r-- | challenge-096/paulo-custodio/c/ch-1.c | 59 | ||||
| -rw-r--r-- | challenge-096/paulo-custodio/c/ch-2.c | 164 | ||||
| -rw-r--r-- | challenge-096/paulo-custodio/cpp/ch-1.cpp | 42 | ||||
| -rw-r--r-- | challenge-096/paulo-custodio/cpp/ch-2.cpp | 150 | ||||
| -rw-r--r-- | challenge-096/paulo-custodio/forth/ch-1.fs | 81 | ||||
| -rw-r--r-- | challenge-096/paulo-custodio/forth/ch-2.fs | 153 | ||||
| -rw-r--r-- | challenge-096/paulo-custodio/perl/ch-1.pl | 22 | ||||
| -rw-r--r-- | challenge-096/paulo-custodio/perl/ch-2.pl | 114 | ||||
| -rw-r--r-- | challenge-096/paulo-custodio/perl/ch-2a.pl | 60 | ||||
| -rw-r--r-- | challenge-096/paulo-custodio/python/ch-1.py | 20 | ||||
| -rw-r--r-- | challenge-096/paulo-custodio/python/ch-2.py | 101 | ||||
| -rw-r--r-- | challenge-096/paulo-custodio/test.pl | 67 |
14 files changed, 1228 insertions, 0 deletions
diff --git a/challenge-096/paulo-custodio/basic/ch-1.bas b/challenge-096/paulo-custodio/basic/ch-1.bas new file mode 100644 index 0000000000..4d3951e8db --- /dev/null +++ b/challenge-096/paulo-custodio/basic/ch-1.bas @@ -0,0 +1,57 @@ +' Challenge 096 +' +' TASK #1 › Reverse Words +' Submitted by: Mohammad S Anwar +' You are given a string $S. +' +' Write a script to reverse the order of words in the given string. The string +' may contain leading/trailing spaces. The string may have more than one space +' between words in the string. Print the result without leading/trailing spaces +' and there should be only one space between words. +' +' Example 1: +' Input: $S = "The Weekly Challenge" +' Output: "Challenge Weekly The" + +function join_args() as string + dim text as string + dim i as integer + + i = 1 + do while command(i)<>"" + text &= trim(command(i))+" " + i += 1 + loop + join_args = trim(text) +end function + +sub split_text(words() as string, text as string) + dim p as integer + + redim preserve words(0) + text = trim(text) + p = instr(text, " ") + do while p > 0 + redim preserve words(ubound(words)+1) + words(ubound(words)) = left(text, p-1) + text = trim(mid(text, p+1)) + p = instr(text, " ") + loop + 'store last segment + redim preserve words(ubound(words)+1) + words(ubound(words)) = text +end sub + +sub print_reverse(words() as string) + dim i as integer + + for i = ubound(words) to 1 step -1 + print words(i);" "; + next i + print +end sub + +dim words() as string +split_text words(), join_args() +print_reverse words() + diff --git a/challenge-096/paulo-custodio/basic/ch-2.bas b/challenge-096/paulo-custodio/basic/ch-2.bas new file mode 100644 index 0000000000..b4eb1fa7b6 --- /dev/null +++ b/challenge-096/paulo-custodio/basic/ch-2.bas @@ -0,0 +1,138 @@ +' Challenge 096 +' +' TASK #2 › Edit Distance +' Submitted by: Mohammad S Anwar +' You are given two strings $S1 and $S2. +' +' Write a script to find out the minimum operations required to convert $S1 +' into $S2. The operations can be insert, remove or replace a character. Please +' check out Wikipedia page for more information. +' +' Example 1: +' Input: $S1 = "kitten"; $S2 = "sitting" +' Output: 3 +' +' Operation 1: replace 'k' with 's' +' Operation 2: replace 'e' with 'i' +' Operation 3: insert 'g' at the end +' Example 2: +' Input: $S1 = "sunday"; $S2 = "monday" +' Output: 2 +' +' Operation 1: replace 's' with 'm' +' Operation 2: replace 'u' with 'o' +' +' NOTE: the Wagner-Fischer Distance algorithm builds a table of distances +' from which the operations can be deduced + +const E as integer = 1 +const S as integer = 2 +const SE as integer = 3 + +function min(a as integer, b as integer) as integer + if a < b then + min = a + else + min = b + end if +end function + +function min3(a as integer, b as integer, c as integer) as integer + min3 = min(a, min(b, c)) +end function + +sub wag_fish_dist(a as string, b as string) + dim i as integer, j as integer, stp as integer, subst_cost as integer + dim min_dr as integer, min_delta as integer, dr as integer, delta as integer + + ' define a table where d(i,j) is the Levenshtein distance between + ' first i chars of a and first j chars of b + dim d(len(a)+1, len(b)+1) as integer + + ' source prefixes can be transformed into empty string by dropping chars + for i = 1 to len(a) + d(i,0) = i + next i + + ' target prefixes can be reached from empty source prefix by inserting chars + for j = 1 to len(b) + d(0,j) = j + next j + + ' flood-fill the rest of the table + for j = 1 to len(b) + for i = 1 to len(a) + if mid(a,i,1) = mid(b,j,1) then + subst_cost = 0 + else + subst_cost = 1 + end if + d(i,j) = min3(d(i-1,j)+1, _ ' deletion + d(i,j-1)+1, _ ' insertion + d(i-1,j-1)+subst_cost) ' substitution + next i + next j + + ' distance is in lower bottom cell + print trim(str(d(len(a),len(b)))) + + ' traverse the minimum path + i = 0: j = 0 + do while i < len(a) or j < len(b) + dr = 0: delta = 0: min_dr = 0: min_delta = len(a)+len(b) + + ' search shortest path in priority SE, E, S + if i < len(a) and j < len(b) then + dr = SE: delta = d(i+1,j+1) - d(i,j) + min_dr = dr: min_delta = delta + end if + if j < len(b) then + dr = E: delta = d(i,j+1) - d(i,j) + if delta < min_delta then + min_dr = dr: min_delta = delta + end if + end if + if i < len(a) then + dr = S: delta = d(i+1,j) - d(i,j) + if delta < min_delta then + min_dr = dr: min_delta = delta + end if + end if + + ' apply shortest path and show steps + select case min_dr + case SE + i += 1: j += 1 + if mid(a,i,1) <> mid(b,j,1) then + stp += 1 + print "Operation"; stp; ": replace '"; _ + mid(a,i,1); "' with '"; mid(b,j,1); "'" + end if + case E + j += 1 + stp += 1 + if j = len(b) then + print "Operation"; stp; ": insert '"; _ + mid(b,j,1); "' at end" + else + print "Operation"; stp; ": insert '"; _ + mid(b,j,1); "' at position"; j + end if + case S + i += 1 + stp += 1 + if i = len(a) then + print "Operation"; stp; ": insert '"; _ + mid(a,i,1); "' at end" + else + print "Operation"; stp; ": insert '"; _ + mid(a,i,1); "' at position"; i + end if + case else + assert(false) + end select + loop +end sub + + +wag_fish_dist command(1), command(2) diff --git a/challenge-096/paulo-custodio/c/ch-1.c b/challenge-096/paulo-custodio/c/ch-1.c new file mode 100644 index 0000000000..93d55b1329 --- /dev/null +++ b/challenge-096/paulo-custodio/c/ch-1.c @@ -0,0 +1,59 @@ +/* +Challenge 096 + +TASK #1 › Reverse Words +Submitted by: Mohammad S Anwar +You are given a string $S. + +Write a script to reverse the order of words in the given string. The string +may contain leading/trailing spaces. The string may have more than one space +between words in the string. Print the result without leading/trailing spaces +and there should be only one space between words. + +Example 1: +Input: $S = "The Weekly Challenge" +Output: "Challenge Weekly The" +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +void* check_mem(void* p) { + if (!p) { + fputs("Out of memory", stderr); + exit(EXIT_FAILURE); + } + return p; +} + +int main(int argc, char* argv[]) { + // concatenate all args + char* text = check_mem(strdup("")); + for (int i = 1; i < argc; i++) { + text = check_mem(realloc(text, strlen(text)+strlen(argv[i])+2)); + strcat(text, argv[i]); + strcat(text, " "); + } + + // build list of words + char** words = NULL; + size_t num_words = 0; + const char* sep = " "; + char* word = strtok(text, sep); + while (word) { + words = check_mem(realloc(words, ++num_words * sizeof(char*))); + words[num_words-1] = word; + + word = strtok(NULL, sep); + } + + // print words in reverse order + for (int i = num_words-1; i >= 0; i--) + printf("%s ", words[i]); + printf("\n"); + + // free memory + free(text); + free(words); +} diff --git a/challenge-096/paulo-custodio/c/ch-2.c b/challenge-096/paulo-custodio/c/ch-2.c new file mode 100644 index 0000000000..718bb6018d --- /dev/null +++ b/challenge-096/paulo-custodio/c/ch-2.c @@ -0,0 +1,164 @@ +/* +Challenge 096 + +TASK #2 › Edit Distance +Submitted by: Mohammad S Anwar +You are given two strings $S1 and $S2. + +Write a script to find out the minimum operations required to convert $S1 +into $S2. The operations can be insert, remove or replace a character. Please +check out Wikipedia page for more information. + +Example 1: +Input: $S1 = "kitten"; $S2 = "sitting" +Output: 3 + +Operation 1: replace 'k' with 's' +Operation 2: replace 'e' with 'i' +Operation 3: insert 'g' at the end +Example 2: +Input: $S1 = "sunday"; $S2 = "monday" +Output: 2 + +Operation 1: replace 's' with 'm' +Operation 2: replace 'u' with 'o' + +NOTE: the Wagner-Fischer Distance algorithm builds a table of distances + from which the operations can be deduced +*/ + +#include <assert.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +void* check_mem(void* p) { + if (!p) { + fputs("Out of memory", stderr); + exit(EXIT_FAILURE); + } + return p; +} + +int min(int a, int b) { + return (a < b) ? a : b; +} + +int min3(int a, int b, int c) { + return min(a, min(b, c)); +} + +enum { DIR_NONE, DIR_E, DIR_S, DIR_SE }; + +void wag_fis_dist(const char* a, const char* b) { + int len_a = strlen(a); + int len_b = strlen(b); + + // define a table where d[i][j] is the Levenshtein distance between + // first i chars of a and first j chars of b + int** d = check_mem(calloc(len_a+1, sizeof(int*))); + for (int i = 0; i <= len_a; i++) + d[i] = check_mem(calloc(len_b+1, sizeof(int))); + + // source prefixes can be transformed into empty string by dropping chars + for (int i = 1; i <= len_a; i++) + d[i][0] = i; + + // target prefixes can be reached from empty source prefix by inserting chars + for (int j = 1; j <= len_b; j++) + d[0][j] = j; + + // flood-fill the rest of the table + for (int j = 1; j <= len_b; j++) { + for (int i = 1; i <= len_a; i++) { + int subst_cost = (a[i-1] == b[j-1]) ? 0 : 1; + d[i][j] = min3(d[i-1][j]+1, // deletion + d[i][j-1]+1, // insertion + d[i-1][j-1]+subst_cost); // substitution + } + } + + // distance is in lower bottom cell + printf("%d\n", d[len_a][len_b]); + + // traverse the minimum path + int i = 0, j = 0, step = 0; + while (i < len_a || j < len_b) { + int min_dir = DIR_NONE, min_delta = INT_MAX; + int dir, delta; + + // search shortest path in priority SE, E, S + if (i < len_a && j < len_b) { + dir = DIR_SE; + delta = d[i+1][j+1] - d[i][j]; + if (delta < min_delta) { + min_dir = dir; + min_delta = delta; + } + } + + if (j < len_b) { + dir = DIR_E; + delta = d[i][j+1] - d[i][j]; + if (delta < min_delta) { + min_dir = dir; + min_delta = delta; + } + } + + if (i < len_a) { + dir = DIR_S; + delta = d[i+1][j] - d[i][j]; + if (delta < min_delta) { + min_dir = dir; + min_delta = delta; + } + } + + // apply shortest path and show steps + switch (min_dir) { + case DIR_SE: + i++; j++; + if (a[i-1] != b[j-1]) { + printf("Operation %d: replace '%c' with '%c'\n", + ++step, a[i-1], b[j-1]); + } + break; + case DIR_E: + j++; + if (j == len_b) + printf("Operation %d: insert '%c' at end\n", + ++step, b[j-1]); + else + printf("Operation %d: insert '%c' at position %d\n", + ++step, b[j-1], j); + break; + case DIR_S: + i++; + if (i == len_a) + printf("Operation %d: delete '%c' at end\n", + ++step, a[i-1]); + else + printf("Operation %d: delete '%c' at position %d\n", + ++step, a[i-1], i); + break; + default: + assert(0); + } + } + + // free memory + for (int i = 0; i <= len_a; i++) + free(d[i]); + free(d); +} + +int main(int argc, char* argv[]) { + if (argc != 3) { + fputs("Usage: ch-2 s1 s2", stderr); + return EXIT_FAILURE; + } + + wag_fis_dist(argv[1], argv[2]); +} diff --git a/challenge-096/paulo-custodio/cpp/ch-1.cpp b/challenge-096/paulo-custodio/cpp/ch-1.cpp new file mode 100644 index 0000000000..34f4d79c73 --- /dev/null +++ b/challenge-096/paulo-custodio/cpp/ch-1.cpp @@ -0,0 +1,42 @@ +/* +Challenge 096 + +TASK #1 › Reverse Words +Submitted by: Mohammad S Anwar +You are given a string $S. + +Write a script to reverse the order of words in the given string. The string +may contain leading/trailing spaces. The string may have more than one space +between words in the string. Print the result without leading/trailing spaces +and there should be only one space between words. + +Example 1: +Input: $S = "The Weekly Challenge" +Output: "Challenge Weekly The" +*/ + +#include <iostream> +#include <string> +#include <vector> +#include <sstream> + +int main(int argc, char* argv[]) { + // concatenate all args + std::string text; + for (int i = 1; i < argc; i++) { + text += argv[i]; + text += " "; + } + + // build list of words + std::vector<std::string> words; + std::istringstream iss(text); + std::string word; + while (iss >> word) + words.push_back(word); + + // print words in reverse order + for (auto it = words.rbegin(); it != words.rend(); ++it) + std::cout << *it << " "; + std::cout << std::endl; +} diff --git a/challenge-096/paulo-custodio/cpp/ch-2.cpp b/challenge-096/paulo-custodio/cpp/ch-2.cpp new file mode 100644 index 0000000000..4e545c85a5 --- /dev/null +++ b/challenge-096/paulo-custodio/cpp/ch-2.cpp @@ -0,0 +1,150 @@ +/* +Challenge 096 + +TASK #2 › Edit Distance +Submitted by: Mohammad S Anwar +You are given two strings $S1 and $S2. + +Write a script to find out the minimum operations required to convert $S1 +into $S2. The operations can be insert, remove or replace a character. Please +check out Wikipedia page for more information. + +Example 1: +Input: $S1 = "kitten"; $S2 = "sitting" +Output: 3 + +Operation 1: replace 'k' with 's' +Operation 2: replace 'e' with 'i' +Operation 3: insert 'g' at the end +Example 2: +Input: $S1 = "sunday"; $S2 = "monday" +Output: 2 + +Operation 1: replace 's' with 'm' +Operation 2: replace 'u' with 'o' + +NOTE: the Wagner-Fischer Distance algorithm builds a table of distances + from which the operations can be deduced +*/ + +#include <algorithm> +#include <iostream> +#include <string> +#include <vector> +#include <cassert> +#include <climits> + +int min3(int a, int b, int c) { + return std::min(a, std::min(b, c)); +} + +enum class Dir { None, E, S, SE }; + +void wag_fis_dist(const std::string& a, const std::string& b) { + size_t len_a = a.size(); + size_t len_b = b.size(); + + // define a table where d[i][j] is the Levenshtein distance between + // first i chars of a and first j chars of b + std::vector<std::vector<int>> d; + d.resize(len_a+1); + for (size_t i = 0; i <= len_a; i++) { + d[i].resize(len_b+1); + } + + // source prefixes can be transformed into empty string by dropping chars + for (size_t i = 1; i <= len_a; i++) + d[i][0] = i; + + // target prefixes can be reached from empty source prefix by inserting chars + for (size_t j = 1; j <= len_b; j++) + d[0][j] = j; + + // flood-fill the rest of the table + for (size_t j = 1; j <= len_b; j++) { + for (size_t i = 1; i <= len_a; i++) { + int subst_cost = (a[i-1] == b[j-1]) ? 0 : 1; + d[i][j] = min3(d[i-1][j]+1, // deletion + d[i][j-1]+1, // insertion + d[i-1][j-1]+subst_cost); // substitution + } + } + + // distance is in lower bottom cell + std::cout << d[len_a][len_b] << std::endl; + + // traverse the minimum path + size_t i = 0, j = 0, step = 0; + while (i < len_a || j < len_b) { + Dir min_dir = Dir::None, dir; + int min_delta = INT_MAX, delta; + + // search shortest path in priority SE, E, S + if (i < len_a && j < len_b) { + dir = Dir::SE; + delta = d[i+1][j+1] - d[i][j]; + if (delta < min_delta) { + min_dir = dir; + min_delta = delta; + } + } + + if (j < len_b) { + dir = Dir::E; + delta = d[i][j+1] - d[i][j]; + if (delta < min_delta) { + min_dir = dir; + min_delta = delta; + } + } + + if (i < len_a) { + dir = Dir::S; + delta = d[i+1][j] - d[i][j]; + if (delta < min_delta) { + min_dir = dir; + min_delta = delta; + } + } + + // apply shortest path and show steps + switch (min_dir) { + case Dir::SE: + i++; j++; + if (a[i-1] != b[j-1]) { + std::cout << "Operation " << ++step << ": replace '" + << a[i-1] << "' with '" << b[j-1] << "'" << std::endl; + } + break; + case Dir::E: + j++; + if (j == len_b) + std::cout << "Operation " << ++step << ": insert '" + << b[j-1] << "' at end" << std::endl; + else + std::cout << "Operation " << ++step << ": insert '" + << b[j-1] << "' at position " << j << std::endl; + break; + case Dir::S: + i++; + if (i == len_a) + std::cout << "Operation " << ++step << ": delete '" + << a[i-1] << "' at end" << std::endl; + else + std::cout << "Operation " << ++step << ": delete '" + << a[i-1] << "' at position " << i << std::endl; + break; + default: + assert(0); + } + } +} + +int main(int argc, char* argv[]) { + if (argc != 3) { + std::cerr << "Usage: ch-2 s1 s2" << std::endl; + return EXIT_FAILURE; + } + + wag_fis_dist(argv[1], argv[2]); +} diff --git a/challenge-096/paulo-custodio/forth/ch-1.fs b/challenge-096/paulo-custodio/forth/ch-1.fs new file mode 100644 index 0000000000..d325b88fed --- /dev/null +++ b/challenge-096/paulo-custodio/forth/ch-1.fs @@ -0,0 +1,81 @@ +#! /usr/bin/env gforth + +\ Challenge 096 +\ +\ TASK #1 > Reverse Words +\ Submitted by: Mohammad S Anwar +\ You are given a string $S. +\ +\ Write a script to reverse the order of words in the given string. The string +\ may contain leading/trailing spaces. The string may have more than one space +\ between words in the string. Print the result without leading/trailing spaces +\ and there should be only one space between words. +\ +\ Example 1: +\ Input: $S = "The Weekly Challenge" +\ Output: "Challenge Weekly The" + +\ trim spaces from start of string +: trim_start ( addr len -- addr len ) + BEGIN + DUP 0= IF EXIT THEN \ exit if lenth is zero + OVER C@ BL = IF \ if starts with space + 1 /STRING \ remove first char + ELSE + EXIT \ no more spaces + THEN + AGAIN ; + +\ trim spaces from start and end of string +: trim ( addr len -- addr len ) + trim_start -TRAILING ; + + +\ clear counted string at PAD +: clear_pad ( -- ) + 0 PAD C! ; + +\ concatenate string to PAD, output as counted string +: append_pad ( addr len -- ) + PAD COUNT + ( src len dest ) \ get address of char after string + 2DUP + >R ( R: end of joined string ) + SWAP CMOVE \ copy string + R> PAD 1+ - PAD C! ; \ store new length + +\ concatenate args from command line to one string in PAD +: join_args ( -- ) + clear_pad + BEGIN NEXT-ARG DUP 0> WHILE \ while input argument + trim append_pad \ trim argument and cat + s" " append_pad \ cat one space + REPEAT 2DROP + PAD COUNT -TRAILING SWAP 1- C! ; \ remove ending spaces + +\ split a string by blanks +\ stores result in dictionary and reclaims memory +: split ( addr len -- tokens count ) + HERE >R \ save dictionary pointer + BEGIN + trim \ trim start and end spaces + 2DUP 2, \ save rest of string; length will be + \ adjusted later + s" " SEARCH ( addr-space len-rest f-found ) + WHILE \ while found space + DUP NEGATE HERE 2 CELLS - +! \ adjust last token length + REPEAT 2DROP \ drop string + R> HERE OVER - ( addr-tokens size-bytes ) + DUP NEGATE ALLOT \ reclaim dictionary + 2 CELLS / ; ( tokens count ) + +\ print the reversed list of tokens +: .tokens ( tokens count -- ) + SWAP OVER 2 CELLS * + SWAP ( addr-end count ) + 0 ?DO \ for each token + 2 CELLS - + DUP 2@ TYPE SPACE + LOOP DROP ; + +join_args \ join arguments to PAD +PAD COUNT split \ split by spaces +.tokens CR \ print in reverse order +BYE diff --git a/challenge-096/paulo-custodio/forth/ch-2.fs b/challenge-096/paulo-custodio/forth/ch-2.fs new file mode 100644 index 0000000000..1ecbbf62c3 --- /dev/null +++ b/challenge-096/paulo-custodio/forth/ch-2.fs @@ -0,0 +1,153 @@ +#! /usr/bin/env gforth + +\ Challenge 096 +\ +\ TASK #2 > Edit Distance +\ Submitted by: Mohammad S Anwar +\ You are given two strings $S1 and $S2. +\ +\ Write a script to find out the minimum operations required to convert $S1 +\ into $S2. The operations can be insert, remove or replace a character. Please +\ check out Wikipedia page for more information. +\ +\ Example 1: +\ Input: $S1 = "kitten"; $S2 = "sitting" +\ Output: 3 +\ +\ Operation 1: replace 'k' with 's' +\ Operation 2: replace 'e' with 'i' +\ Operation 3: insert 'g' at the end +\ Example 2: +\ Input: $S1 = "sunday"; $S2 = "monday" +\ Output: 2 +\ +\ Operation 1: replace 's' with 'm' +\ Operation 2: replace 'u' with 'o' +\ +\ NOTE: the Wagner-Fischer Distance algorithm builds a table of distances +\ from which the operations can be deduced + + +\ collect arguments - strings a and b +NEXT-ARG CONSTANT len_a CONSTANT a +NEXT-ARG CONSTANT len_b CONSTANT b + +\ get string char by index (1..N) +: a[]@ ( index -- char ) + a + 1- C@ ; + +: b[]@ ( index -- char ) + b + 1- C@ ; + +\ build array len_a+1 rows, len_b+1 cols +CREATE d +len_a 1+ len_b 1+ * CELLS ALLOT + +\ index array +: d[] ( i j -- addr ) + SWAP len_b 1+ * + CELLS + d + ; + +\ init array to zeros +: clear_d ( -- ) + len_a 1+ 0 ?DO + len_b 1+ 0 ?DO + 0 J I d[] ! + LOOP + LOOP ; + +\ init source column +: init_source ( -- ) + len_a 1+ 1 ?DO + i i 0 d[] ! + LOOP ; + +\ init target row +: init_target ( -- ) + len_b 1+ 1 ?DO + i 0 i d[] ! + LOOP ; + +\ flood-fill table +: flood_fill ( -- ) + len_b 1+ 1 ?DO + len_a 1+ 1 ?DO + i 1- j d[] @ 1+ \ deletion + i j 1- d[] @ 1+ \ insertion + i 1- j 1- d[] @ \ substitution + i a[]@ j b[]@ <> IF 1+ THEN + MIN MIN + i j d[] ! + LOOP + LOOP ; + +\ output number without space +: .num ( n -- ) + 0 U.R ; + +\ output step +0 VALUE step +: show_operation ( -- ) + step 1+ TO step + ." Operation " step .num ." : " ; + +\ traverse minimum path +: traverse_path ( -- ) + 0 0 { i j } \ starting point + BEGIN i len_a < j len_b < OR WHILE + 0 { min_dir } len_a len_b + { min_delta } + 0 { dir } 0 { delta } + + \ search shortest path in priority SE (D), E, S + i len_a < j len_b < AND IF + 'D' TO dir + i 1+ j 1+ d[] @ i j d[] @ - TO delta + delta min_delta < IF + dir TO min_dir + delta TO min_delta + THEN + THEN + + j len_b < IF + 'E' TO dir + i j 1+ d[] @ i j d[] @ - TO delta + delta min_delta < IF + dir TO min_dir + delta TO min_delta + THEN + THEN + + i len_a < IF + 'S' TO dir + i 1+ j d[] @ i j d[] @ - TO delta + delta min_delta < IF + dir TO min_dir + delta TO min_delta + THEN + THEN + + \ apply shortest path and show steps + min_dir 'D' = IF + i 1+ TO i + j 1+ TO j + i a[]@ j b[]@ <> IF + show_operation ." replace '" i a[]@ EMIT ." ' with '" j b[]@ EMIT ." '" CR + THEN + ELSE min_dir 'E' = IF + j 1+ TO j + show_operation ." insert '" j b[]@ EMIT ." ' at " + j len_b = IF ." end" ELSE ." position " j .num THEN CR + ELSE min_dir 'S' = IF + i 1+ TO i + show_operation ." delete '" i a[]@ EMIT ." ' at " + j len_b = IF ." end" ELSE ." position " i .num THEN CR + THEN THEN THEN + REPEAT ; + +: wag_fis_dist ( -- ) + clear_d init_source init_target flood_fill + len_a len_b d[] @ . CR \ show distance + traverse_path ; + +wag_fis_dist +BYE diff --git a/challenge-096/paulo-custodio/perl/ch-1.pl b/challenge-096/paulo-custodio/perl/ch-1.pl new file mode 100644 index 0000000000..dac87ee172 --- /dev/null +++ b/challenge-096/paulo-custodio/perl/ch-1.pl @@ -0,0 +1,22 @@ +#!/usr/bin/env perl + +# Challenge 096 +# +# TASK #1 › Reverse Words +# Submitted by: Mohammad S Anwar +# You are given a string $S. +# +# Write a script to reverse the order of words in the given string. The string +# may contain leading/trailing spaces. The string may have more than one space +# between words in the string. Print the result without leading/trailing spaces +# and there should be only one space between words. +# +# Example 1: +# Input: $S = "The Weekly Challenge" +# Output: "Challenge Weekly The" + +use strict; +use warnings; +use 5.030; + +say join ' ', reverse split ' ', "@ARGV"; diff --git a/challenge-096/paulo-custodio/perl/ch-2.pl b/challenge-096/paulo-custodio/perl/ch-2.pl new file mode 100644 index 0000000000..577366f8f3 --- /dev/null +++ b/challenge-096/paulo-custodio/perl/ch-2.pl @@ -0,0 +1,114 @@ +#!/usr/bin/env perl + +# Challenge 096 +# +# TASK #2 › Edit Distance +# Submitted by: Mohammad S Anwar +# You are given two strings $S1 and $S2. +# +# Write a script to find out the minimum operations required to convert $S1 +# into $S2. The operations can be insert, remove or replace a character. Please +# check out Wikipedia page for more information. +# +# Example 1: +# Input: $S1 = "kitten"; $S2 = "sitting" +# Output: 3 +# +# Operation 1: replace 'k' with 's' +# Operation 2: replace 'e' with 'i' +# Operation 3: insert 'g' at the end +# Example 2: +# Input: $S1 = "sunday"; $S2 = "monday" +# Output: 2 +# +# Operation 1: replace 's' with 'm' +# Operation 2: replace 'u' with 'o' + +# NOTE: the Wagner-Fischer Distance algorithm builds a table of distances +# from which the operations can be deduced + +use strict; +use warnings; +use 5.030; +use List::Util 'min'; + +use Data::Dump 'dump'; + +wag_fis_dist(@ARGV); + +sub wag_fis_dist { + my($a, $b) = @_; + + # define a table where d[i,j] is the Levenshtein distance between + # first i chars of $a and first j chars of $b + my @d; + $d[0][0] = 0; + + # source prefixes can be transformed into empty string by dropping chars + $d[$_][0] = $_ for (1 .. length($a)); + + # target prefixes can be reached from empty source prefix by inserting chars + $d[0][$_] = $_ for (1 .. length($b)); + + # flood-fill the rest of the table + for my $j (1 .. length($b)) { + for my $i (1 .. length($a)) { + my $subst_cost = + (substr($a,$i-1,1) eq substr($b,$j-1,1)) ? 0 : 1; + $d[$i][$j] = min($d[$i-1][$j]+1, # deletion + $d[$i][$j-1]+1, # insertion + $d[$i-1][$j-1]+$subst_cost); # substitution + } + } + + # distance is in lower bottom cell + say $d[length($a)][length($b)]; + + # traverse the minimum path + my($i, $j, $step) = (0,0,0); + while ($i < length($a) || $j < length($b)) { + my($min_dir, $min_delta) = ('', 1e10); + my($dir, $delta); + + # search shortest path in priority SE, E, S + if ($i < length($a) && $j < length($b)) { + ($dir, $delta) = ("SE", $d[$i+1][$j+1] - $d[$i][$j]); + ($min_dir, $min_delta) = ($dir, $delta) if $delta < $min_delta; + } + + if ($j < length($b)) { + ($dir, $delta) = ("E", $d[$i][$j+1] - $d[$i][$j]); + ($min_dir, $min_delta) = ($dir, $delta) if $delta < $min_delta; + } + + if ($i < length($a)) { + ($dir, $delta) = ("S", $d[$i+1][$j] - $d[$i][$j]); + ($min_dir, $min_delta) = ($dir, $delta) if $delta < $min_delta; + } + + # apply shortest path and show steps + if ($min_dir eq "SE") { + ($i, $j) = ($i+1, $j+1); + my $from = substr($a,$i-1,1); + my $to = substr($b,$j-1,1); + if ($from ne $to) { + say "Operation ", ++$step, ": replace '$from' with '$to'"; + } + } + elsif ($min_dir eq "E") { + ($i, $j) = ($i, $j+1); + my $add = substr($b,$j-1,1); + say "Operation ", ++$step, ": insert '$add' ", + ($j==length($b)) ? "at end" : "at position $j"; + } + elsif ($min_dir eq "S") { + ($i, $j) = ($i+1, $j); + my $del = substr($a,$i-1,1); + say "Operation ", ++$step, ": delete '$del' ", + ($i==length($a)) ? "at end" : "at position $i"; + } + else { + die $min_dir; + } + } +} diff --git a/challenge-096/paulo-custodio/perl/ch-2a.pl b/challenge-096/paulo-custodio/perl/ch-2a.pl new file mode 100644 index 0000000000..9d253572aa --- /dev/null +++ b/challenge-096/paulo-custodio/perl/ch-2a.pl @@ -0,0 +1,60 @@ +#!/usr/bin/env perl + +# Challenge 096 +# +# TASK #2 › Edit Distance +# Submitted by: Mohammad S Anwar +# You are given two strings $S1 and $S2. +# +# Write a script to find out the minimum operations required to convert $S1 +# into $S2. The operations can be insert, remove or replace a character. Please +# check out Wikipedia page for more information. +# +# Example 1: +# Input: $S1 = "kitten"; $S2 = "sitting" +# Output: 3 +# +# Operation 1: replace 'k' with 's' +# Operation 2: replace 'e' with 'i' +# Operation 3: insert 'g' at the end +# Example 2: +# Input: $S1 = "sunday"; $S2 = "monday" +# Output: 2 +# +# Operation 1: replace 's' with 'm' +# Operation 2: replace 'u' with 'o' + +# NOTE: the Levenshtein Distance algorithm only computes distance, does not output +# the edit steps + +use strict; +use warnings; +use 5.030; +use List::Util 'min'; + +# avoid repeated recursive calls of lev_dist +# for the Example 1 reduces from 29737 to 56 calls! +use Memoize; +memoize('lev_dist'); + +say lev_dist(@ARGV); + + +sub lev_dist { + my($a, $b) = @_; + + if ($a eq '') { + return length($b); + } + elsif ($b eq '') { + return length($a); + } + else { + my $ch1_diff = substr($a,0,1) ne substr($b,0,1) ? 1 : 0; + return min( + lev_dist(substr($a, 1), $b)+1, # deletion + lev_dist($a, substr($b, 1))+1, # insertion + lev_dist(substr($a, 1), substr($b, 1))+$ch1_diff # substitution + ); + } +} |
