Longest common suffix: Difference between revisions

From Rosetta Code
Content added Content deleted
(→‎{{header|JavaScript}}: Added a draft in JavaScript)
m (→‎AppleScript :: Functional: Tidier test format.)
Line 157: Line 157:
on run
on run
script test
script test
on |λ|(xs)
on |λ|(s)
set xs to words of s
showList(xs) & " -> '" & longestCommonSuffix(xs) & "'"
showList(xs) & " -> '" & longestCommonSuffix(xs) & "'"
end |λ|
end |λ|
Line 163: Line 164:
unlines(map(test, {¬
unlines(map(test, {¬
{"throne", "sousaphone", "tone"}, ¬
"throne sousaphone tone", ¬
{"prefix", "suffix", "infix"}, ¬
"prefix suffix infix", ¬
{"remark", "spark", "aardvark", "lark"}, ¬
"remark spark aardvark lark", ¬
{"ectoplasm", "banana", "brick"}}))
"ectoplasm banana brick"}))
end run
end run



Revision as of 11:18, 8 August 2020

Longest common suffix is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.
Task

The goal is to write a function to find the longest common suffix string amongst an array of strings.

ALGOL 68

Works with: ALGOL 68G version Any - tested with release 2.8.3.win32

Based on the Algol 68 sample for the Longest Common Prefix task. <lang algol68># find the longest common suffix of two strings # PRIO COMMONSUFFIX = 1; OP COMMONSUFFIX = ( STRING a, b )STRING:

   BEGIN
       INT a pos := UPB a; INT a min = LWB a;
       INT b pos := UPB b; INT b min = LWB b;
       WHILE
           IF a pos < a min OR b pos < b min THEN FALSE
           ELSE a[ a pos ] = b[ b pos ]
           FI
       DO
           a pos -:= 1; b pos -:= 1
       OD;
       a[ a pos + 1 : UPB a ]
   END # COMMONSUFFIX # ;
  1. get the length of a string #

OP LEN = ( STRING a )INT: ( UPB a + 1 ) - LWB a;

  1. find the longest common suffix of an array of STRINGs #

OP LONGESTSUFFIX = ( []STRING list )STRING:

   IF  UPB list < LWB list
   THEN
       # no elements #
       ""
   ELIF UPB list = LWB list
   THEN
       # only one element #
       list[ LWB list ]
   ELSE
       # more than one element #
       STRING suffix := list[ LWB list ] COMMONSUFFIX list[ 1 + LWB list ];
       FOR pos FROM 2 + LWB list TO UPB list WHILE suffix /= "" DO
           STRING next suffix := list[ pos ] COMMONSUFFIX suffix;
           IF LEN next suffix < LEN suffix
           THEN
               # this element has a smaller common suffix #
               suffix := next suffix
           FI
       OD;
       suffix
   FI ;
  1. test the LONGESTSUFFIX operator #

PROC test suffix = ( []STRING list, STRING expected result )VOID:

   BEGIN
       STRING suffix = LONGESTSUFFIX list;
       print( ( "longest common suffix of (" ) );
       FOR pos FROM LWB list TO UPB list DO print( ( " """, list[ pos ], """" ) ) OD;
       print( ( " ) is: """, suffix, """ "
              , IF suffix = expected result THEN "as expected" ELSE "NOT AS EXPECTED" FI 
              , newline
              )
            )
   END # test suffix # ;

[ 1 : 0 ]STRING empty list; # for recent versions of Algol 68G, can't just put "()" for an empty list # BEGIN

   test suffix( ( "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ), "day" );
   test suffix( ( "throne", "throne" ), "throne" );
   test suffix( ( "throne", "dungeon" ), "" );
   test suffix( ( "throne", "", "throne" ), "" );
   test suffix( ( "cheese" ), "cheese" );
   test suffix( ( "" ), "" );
   test suffix( empty list, "" );
   test suffix( ( "prefix", "suffix" ), "fix" );
   test suffix( ( "send", "lend" ), "end" )

END</lang>

Output:
longest common suffix of ( "Sunday" "Monday" "Tuesday" "Wednesday" "Thursday" "Friday" "Saturday" ) is: "day" as expected
longest common suffix of ( "throne" "throne" ) is: "throne" as expected
longest common suffix of ( "throne" "dungeon" ) is: "" as expected
longest common suffix of ( "throne" "" "throne" ) is: "" as expected
longest common suffix of ( "cheese" ) is: "cheese" as expected
longest common suffix of ( "" ) is: "" as expected
longest common suffix of ( ) is: "" as expected
longest common suffix of ( "prefix" "suffix" ) is: "fix" as expected
longest common suffix of ( "send" "lend" ) is: "end" as expected

AppleScript

Procedural

The simplest solution in AppleScript seems to be to reverse the strings, apply the AppleScriptObjC solution for the Longest common prefix task, and reverse the result.

<lang applescript>use AppleScript version "2.4" -- OS X 10.10 (Yosemite) or later use framework "Foundation"

on longestCommonSuffix(textList)

   -- Eliminate any non-texts from the input.
   if (textList's class is record) then return ""
   set textList to (textList as list)'s text
   if (textList is {}) then return ""
   
   set astid to AppleScript's text item delimiters
   set AppleScript's text item delimiters to ""
   repeat with i from 1 to (count textList)
       set item i of textList to (reverse of characters of item i of textList) as text
   end repeat
   set lcs to (reverse of characters of longestCommonPrefix(textList)) as text
   set AppleScript's text item delimiters to astid
   
   return lcs

end longestCommonSuffix

on longestCommonPrefix(textList)

   -- Eliminate any non-texts from the input.
   if (textList's class is record) then return ""
   set textList to (textList as list)'s text
   if (textList is {}) then return ""
   
   -- Convert the AppleScript list to an NSArray of NSStrings.
   set stringArray to current application's class "NSArray"'s arrayWithArray:(textList)
   
   -- Compare the strings case-insensitively using a built-in NSString method.
   set lcp to stringArray's firstObject()
   repeat with i from 2 to (count stringArray)
       set lcp to (lcp's commonPrefixWithString:(item i of stringArray) options:(current application's NSCaseInsensitiveSearch))
       if (lcp's |length|() is 0) then exit repeat
   end repeat
   
   -- Return the NSString result as AppleScript text.
   return lcp as text

end longestCommonPrefix

-- Tests and results: longestCommonSuffix({"throne", "sousaphone"}) --> "one" longestCommonSuffix({"prefix", "suffix"}) --> "fix" longestCommonSuffix({"remark", "spark", "aardvark"}) --> "ark" longestCommonSuffix({"ectoplasm", "banana"}) --> ""</lang>

Functional

and for more productivity, and higher re-use of library functions, we can write a functional definition (rather than a procedure):

<lang applescript>------------------- LONGEST COMMON SUFFIX ------------------


-- longestCommonSuffix :: [String] -> String on longestCommonSuffix(xs)

   if 1 < length of xs then
       reverse of map(my fst, ¬
           takeWhile(my allSame, ¬
               transpose(map(my |reverse|, xs)))) as text
   else
       xs as text
   end if

end longestCommonSuffix



TESTS --------------------------

on run

   script test
       on |λ|(s)
           set xs to words of s
           showList(xs) & " -> '" & longestCommonSuffix(xs) & "'"
       end |λ|
   end script
   
   unlines(map(test, {¬
       "throne sousaphone tone", ¬
       "prefix suffix infix", ¬
       "remark spark aardvark lark", ¬
       "ectoplasm banana brick"}))

end run



GENERIC FUNCTIONS --------------------

-- all :: (a -> Bool) -> [a] -> Bool on all(p, xs)

   -- True if p holds for every value in xs
   tell mReturn(p)
       set lng to length of xs
       repeat with i from 1 to lng
           if not |λ|(item i of xs, i, xs) then return false
       end repeat
       true
   end tell

end all


-- allSame :: [a] -> Bool on allSame(xs)

   if 2 > length of xs then
       true
   else
       script p
           property h : item 1 of xs
           on |λ|(x)
               h = x
           end |λ|
       end script
       all(p, rest of xs)
   end if

end allSame


-- comparing :: (a -> b) -> (a -> a -> Ordering) on comparing(f)

   script
       on |λ|(a, b)
           tell mReturn(f)
               set fa to |λ|(a)
               set fb to |λ|(b)
               if fa < fb then
                   -1
               else if fa > fb then
                   1
               else
                   0
               end if
           end tell
       end |λ|
   end script

end comparing


-- concatMap :: (a -> [b]) -> [a] -> [b] on concatMap(f, xs)

   set lng to length of xs
   set acc to {}
   tell mReturn(f)
       repeat with i from 1 to lng
           set acc to acc & (|λ|(item i of xs, i, xs))
       end repeat
   end tell
   return acc

end concatMap


-- foldl :: (a -> b -> a) -> a -> [b] -> a on foldl(f, startValue, xs)

   tell mReturn(f)
       set v to startValue
       set lng to length of xs
       repeat with i from 1 to lng
           set v to |λ|(v, item i of xs, i, xs)
       end repeat
       return v
   end tell

end foldl


-- fst :: (a, b) -> a on fst(tpl)

   if class of tpl is record then
       |1| of tpl
   else
       item 1 of tpl
   end if

end fst


-- intercalate :: String -> [String] -> String on intercalate(delim, xs)

   set {dlm, my text item delimiters} to ¬
       {my text item delimiters, delim}
   set str to xs as text
   set my text item delimiters to dlm
   str

end intercalate


-- justifyLeft :: Int -> Char -> String -> String on justifyLeft(n, cFiller)

   script
       on |λ|(strText)
           if n > length of strText then
               text 1 thru n of (strText & replicate(n, cFiller))
           else
               strText
           end if
       end |λ|
   end script

end justifyLeft


-- length :: [a] -> Int on |length|(xs)

   set c to class of xs
   if list is c or string is c then
       length of xs
   else
       (2 ^ 29 - 1) -- (maxInt - simple proxy for non-finite)
   end if

end |length|


-- map :: (a -> b) -> [a] -> [b] on map(f, xs)

   -- The list obtained by applying f
   -- to each element of xs.
   tell mReturn(f)
       set lng to length of xs
       set lst to {}
       repeat with i from 1 to lng
           set end of lst to |λ|(item i of xs, i, xs)
       end repeat
       return lst
   end tell

end map


-- maximumBy :: (a -> a -> Ordering) -> [a] -> a on maximumBy(f, xs)

   set cmp to mReturn(f)
   script max
       on |λ|(a, b)
           if a is missing value or cmp's |λ|(a, b) < 0 then
               b
           else
               a
           end if
       end |λ|
   end script
   
   foldl(max, missing value, xs)

end maximumBy


-- min :: Ord a => a -> a -> a on min(x, y)

   if y < x then
       y
   else
       x
   end if

end min


-- mReturn :: First-class m => (a -> b) -> m (a -> b) on mReturn(f)

   -- 2nd class handler function lifted into 1st class script wrapper. 
   if script is class of f then
       f
   else
       script
           property |λ| : f
       end script
   end if

end mReturn


-- Egyptian multiplication - progressively doubling a list, appending -- stages of doubling to an accumulator where needed for binary -- assembly of a target length -- replicate :: Int -> a -> [a] on replicate(n, a)

   set out to {}
   if 1 > n then return out
   set dbl to {a}
   
   repeat while (1 < n)
       if 0 < (n mod 2) then set out to out & dbl
       set n to (n div 2)
       set dbl to (dbl & dbl)
   end repeat
   return out & dbl

end replicate


-- reverse :: [a] -> [a] on |reverse|(xs)

   if class of xs is text then
       (reverse of characters of xs) as text
   else
       reverse of xs
   end if

end |reverse|


-- showList :: [a] -> String on showList(xs)

   script show
       on |λ|(x)
           if text is class of x then
               "'" & x & "'"
           else
               x as text
           end if
       end |λ|
   end script
   if {} ≠ xs then
       "[" & intercalate(", ", map(show, xs)) & "]"
   else
       "[]"
   end if

end showList


-- take :: Int -> [a] -> [a] -- take :: Int -> String -> String on take(n, xs)

   set c to class of xs
   if list is c then
       if 0 < n then
           items 1 thru min(n, length of xs) of xs
       else
           {}
       end if
   else if string is c then
       if 0 < n then
           text 1 thru min(n, length of xs) of xs
       else
           ""
       end if
   else if script is c then
       set ys to {}
       repeat with i from 1 to n
           set v to |λ|() of xs
           if missing value is v then
               return ys
           else
               set end of ys to v
           end if
       end repeat
       return ys
   else
       missing value
   end if

end take


-- takeWhile :: (a -> Bool) -> [a] -> [a] -- takeWhile :: (Char -> Bool) -> String -> String on takeWhile(p, xs)

   if script is class of xs then
       takeWhileGen(p, xs)
   else
       tell mReturn(p)
           repeat with i from 1 to length of xs
               if not |λ|(item i of xs) then ¬
                   return take(i - 1, xs)
           end repeat
       end tell
       return xs
   end if

end takeWhile


-- transpose :: a -> a on transpose(rows)

   set w to length of maximumBy(comparing(|length|), rows)
   set paddedRows to map(justifyLeft(w, "x"), rows)
   
   script cols
       on |λ|(_, iCol)
           script cell
               on |λ|(row)
                   item iCol of row
               end |λ|
           end script
           concatMap(cell, paddedRows)
       end |λ|
   end script
   
   map(cols, item 1 of rows)

end transpose


-- unlines :: [String] -> String on unlines(xs)

   -- A single string formed by the intercalation
   -- of a list of strings with the newline character.
   set {dlm, my text item delimiters} to ¬
       {my text item delimiters, linefeed}
   set str to xs as text
   set my text item delimiters to dlm
   str

end unlines</lang>

Output:
['throne', 'sousaphone', 'tone'] -> 'one'
['prefix', 'suffix', 'infix'] -> 'fix'
['remark', 'spark', 'aardvark', 'lark'] -> 'ark'
['ectoplasm', 'banana', 'brick'] -> ''

Delphi

Library: Types
Translation of: Ring

<lang Delphi> program Longest_common_suffix;

{$APPTYPE CONSOLE}

uses

 System.SysUtils,
 Types;

type

 TStringDynArrayHelper = record helper for TStringDynArray
 private
   class function Compare(const s: string; a: TStringDynArray; subSize: integer):
     Boolean;
 public
   function Reverse(value: string): string;
   function LongestSuffix: string;
   function Join(const sep: char): string;
 end;

{ TStringDynArrayHelper }

class function TStringDynArrayHelper.Compare(const s: string; a: TStringDynArray;

 subSize: integer): Boolean;

var

 i: Integer;

begin

 for i := 0 to High(a) do
   if s <> a[i].Substring(0, subSize) then
     exit(False);
 Result := True;

end;

function TStringDynArrayHelper.Join(const sep: char): string; begin

 Result := string.Join(sep, self);

end;

function TStringDynArrayHelper.LongestSuffix: string; var

 ALength: Integer;
 i, lenMin, longest: Integer;
 ref: string;

begin

 ALength := Length(self);
 // Empty list
 if ALength = 0 then
   exit();
 lenMin := MaxInt;
 for i := 0 to ALength - 1 do
 begin
   // One string is empty
   if self[i].IsEmpty then
     exit();
   self[i] := Reverse(self[i]);
   // Get the minimum length of string
   if lenMin > self[i].Length then
     lenMin := self[i].Length;
 end;
 longest := -1;
 repeat
   inc(longest);
   ref := self[0].Substring(0, longest + 1);
 until not compare(ref, Self, longest + 1) or (longest >= lenMin);
 Result := self[0].Substring(0, longest);
 Result := reverse(Result);

end;

function TStringDynArrayHelper.Reverse(value: string): string; var

 ALength: Integer;
 i: Integer;
 c: Char;

begin

 ALength := value.Length;
 Result := value;
 if ALength < 2 then
   exit;
 for i := 1 to ALength div 2 do
 begin
   c := Result[i];
   Result[i] := Result[ALength - i + 1];
   Result[ALength - i + 1] := c;
 end;

end;

var

 List: TStringDynArray;

begin

 List := ['baabababc', 'baabc', 'bbbabc'];
 Writeln('Input:');
 Writeln(List.Join(#10), #10);
 Writeln('Longest common suffix = ', List.LongestSuffix);
 Readln;

end.

</lang>

Output:
Input:
baabababc
baabc
bbbabc

Longest common suffix = abc

Factor

Works with: Factor version 0.99 2020-07-03

<lang factor>USING: accessors grouping kernel prettyprint sequences sequences.extras ;

! Like take-while, but for matrices and works from the rear.

take-col-while-last ( ... matrix quot: ( ... col -- ... ? ) -- ... new-matrix )
   [ [ <reversed> ] map flip ] dip take-while ; inline
lcs ( seq -- lcs )
   dup first swap [ all-equal? ] take-col-while-last to>> tail* ;

{ "baabababc" "baabc" "bbbabc" } lcs . { "baabababc" "baabc" "bbbazc" } lcs . { "" } lcs .</lang>

Output:
"abc"
"c"
""

Go

Translation of: Wren

<lang go>package main

import (

   "fmt"
   "strings"

)

func lcs(a []string) string {

   le := len(a)
   if le == 0 {
       return ""
   }
   if le == 1 {
       return a[0]
   }
   le0 := len(a[0])
   minLen := le0
   for i := 1; i < le; i++ {
       if len(a[i]) < minLen {
           minLen = len(a[i])
       }
   }
   if minLen == 0 {
       return ""
   }
   res := ""
   a1 := a[1:]
   for i := 1; i <= minLen; i++ {
       suffix := a[0][le0-i:]
       for _, e := range a1 {
           if !strings.HasSuffix(e, suffix) {
               return res
           }
       }
       res = suffix
   }
   return res

}

func main() {

   tests := [][]string{
       {"baabababc", "baabc", "bbbabc"},
       {"baabababc", "baabc", "bbbazc"},
       {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"},
       {"longest", "common", "suffix"},
       {"suffix"},
       {""},
   }
   for _, test := range tests {
       fmt.Printf("%v -> \"%s\"\n", test, lcs(test))
   }

}</lang>

Output:
[baabababc baabc bbbabc] -> "abc"
[baabababc baabc bbbazc] -> "c"
[Sunday Monday Tuesday Wednesday Thursday Friday Saturday] -> "day"
[longest common suffix] -> ""
[suffix] -> "suffix"
[] -> ""

Haskell

This task clearly needs a little more work to bring it up to the usual standard – it's rather underspecified, and bereft of test samples – but one response, for the moment, might be something like: <lang haskell>import Data.List (transpose)

longestCommonSuffix :: [String] -> String longestCommonSuffix =

 foldr (flip (<>) . return . head) [] .
 takeWhile (all =<< (==) . head) . transpose . fmap reverse

main :: IO () main =

 mapM_
   (putStrLn . longestCommonSuffix)
   [ [ "Sunday"
     , "Monday"
     , "Tuesday"
     , "Wednesday"
     , "Thursday"
     , "Friday"
     , "Saturday"
     ]
   , [ "Sondag"
     , "Maandag"
     , "Dinsdag"
     , "Woensdag"
     , "Donderdag"
     , "Vrydag"
     , "Saterdag"
     , "dag"
     ]
   ]</lang>
Output:
day
dag


JavaScript

<lang javascript>(() => {

   'use strict';
   // --------------- LONGEST COMMON SUFFIX ---------------
   // longestCommonSuffix :: [String] -> String
   const longestCommonSuffix = xs => (
           1 < xs.length ? (
               takeWhile(allSame)(
                   transpose(xs.map(reverse))
               ).map(fst).reverse()
           ) : xs
       ).join();


   // ----------------------- TEST ------------------------
   const main = () =>
       fTable('Longest common suffix:\n')(unwords)(
           quoted("'")
       )(longestCommonSuffix)([
           'throne saxophone tone',
           'prefix suffix infix',
           'remark spark aardvark lark',
           'ectoplasm banana brick'
       ].map(words))


   // ----------------- GENERIC FUNCTIONS -----------------
   // allSame :: [a] -> Bool
   const allSame = xs =>
       2 > xs.length || (
           h => xs.slice(1).every(x => h === x)
       )(xs[0]);


   // fst :: (a, b) -> a
   const fst = tpl =>
       // First member of a pair.
       tpl[0];


   // fTable :: String -> (a -> String) -> 
   // (b -> String) -> (a -> b) -> [a] -> String
   const fTable = s =>
       // Heading -> x display function ->
       //           fx display function ->
       //    f -> values -> tabular string
       xShow => fxShow => f => xs => {
           const
               ys = xs.map(xShow),
               w = Math.max(...ys.map(length));
           return s + '\n' + zipWith(
               a => b => a.padStart(w, ' ') + ' -> ' + b
           )(ys)(
               xs.map(x => fxShow(f(x)))
           ).join('\n');
       };


   // length :: [a] -> Int
   const length = xs =>
       // Returns Infinity over objects without finite
       // length. This enables zip and zipWith to choose
       // the shorter argument when one is non-finite,
       // like cycle, repeat etc
       'GeneratorFunction' !== xs.constructor
       .constructor.name ? xs.length : Infinity;


   // map :: (a -> b) -> [a] -> [b]
   const map = f =>
       // The list obtained by applying f
       // to each element of xs.
       // (The image of xs under f).
       xs => [...xs].map(f);


   // reverse :: [a] -> [a]
   const reverse = xs =>
       'string' !== typeof xs ? (
           xs.slice(0).reverse()
       ) : xs.split().reverse().join();


   // str :: a -> String
   const str = x =>
       Array.isArray(x) && x.every(
           v => ('string' === typeof v) && (1 === v.length)
       ) ? (
           x.join()
       ) : x.toString();


   // take :: Int -> [a] -> [a]
   // take :: Int -> String -> String
   const take = n =>
       // The first n elements of a list,
       // string of characters, or stream.
       xs => 'GeneratorFunction' !== xs
       .constructor.constructor.name ? (
           xs.slice(0, n)
       ) : [].concat.apply([], Array.from({
           length: n
       }, () => {
           const x = xs.next();
           return x.done ? [] : [x.value];
       }));


   // takeWhile :: (a -> Bool) -> [a] -> [a]
   // takeWhile :: (Char -> Bool) -> String -> String
   const takeWhile = p =>
       xs => (
           n => xs.slice(
               0, 0 < n ? until(
                   i => n === i || !p(xs[i])
               )(i => 1 + i)(0) : 0
           )
       )(xs.length);


   // transpose :: a -> a
   const transpose = xss => {
       // If some of the rows are shorter than the following rows, 
       // their elements are skipped:
       // > transpose [[10,11],[20],[],[30,31,32]] == [[10,20,30],[11,31],[32]]
       const go = xss =>
           0 < xss.length ? (() => {
               const
                   h = xss[0],
                   t = xss.slice(1);
               return 0 < h.length ? [
                   [h[0]].concat(t.reduce(
                       (a, xs) => a.concat(
                           0 < xs.length ? (
                               [xs[0]]
                           ) : []
                       ),
                       []
                   ))
               ].concat(go([h.slice(1)].concat(
                   t.map(xs => xs.slice(1))
               ))) : go(t);
           })() : [];
       return go(xss);
   };


   // until :: (a -> Bool) -> (a -> a) -> a -> a
   const until = p => f => x => {
       let v = x;
       while (!p(v)) v = f(v);
       return v;
   };
   // unwords :: [String] -> String
   const unwords = xs =>
       // A space-separated string derived
       // from a list of words.
       xs.join(' ');


   // quoted :: Char -> String -> String
   const quoted = c =>
       // A string flanked on both sides
       // by a specified quote character.
       s => c + s + c;
   // words :: String -> [String]
   const words = s =>
       // List of space-delimited sub-strings.
       s.split(/\s+/);


   // zipWithList :: (a -> b -> c) -> [a] -> [b] -> [c]
   const zipWith = f =>
       // A list constructed by zipping with a
       // custom function, rather than with the
       // default tuple constructor.
       xs => ys => ((xs_, ys_) => {
           const lng = Math.min(length(xs_), length(ys_));
           return take(lng)(xs_).map(
               (x, i) => f(x)(ys_[i])
           );
       })([...xs], [...ys]);
   // MAIN ---
   return main();

})();</lang>

Output:
Longest common suffix:

     throne saxophone tone -> 'one'
       prefix suffix infix -> 'fix'
remark spark aardvark lark -> 'ark'
    ectoplasm banana brick -> ''

Julia

<lang julia>function longestcommonsuffix(strings)

   n, nmax = 0, minimum(length, strings)
   nmax == 0 && return ""
   while n <= nmax && all(s -> s[end-n] == strings[end][end-n], strings)
       n += 1
   end
   return strings[1][end-n+1:end]

end

println(longestcommonsuffix(["baabababc","baabc","bbbabc"])) println(longestcommonsuffix(["baabababc","baabc","bbbazc"]))

println(longestcommonsuffix([""]))</lang>

Output:
abc
c

Perl

Based on Longest_common_prefix Perl entry. <lang perl>use strict; use warnings; use feature 'say';

sub lcs {

   for (0..$#_) { $_[$_] = join , reverse split , $_[$_] }
   join , reverse split , (join("\0", @_) =~ /^ ([^\0]*) [^\0]* (?:\0 \1 [^\0]*)* $/sx)[0];

}

for my $words (

 [ <Sunday Monday Tuesday Wednesday Thursday Friday Saturday> ],
 [ <Sondag Maandag Dinsdag Woensdag Donderdag Vrydag Saterdag dag> ],
 [ 2347, 9312347, 'acx5g2347', 12.02347 ],
 [ <longest common suffix> ],
 [ ('one, Hey!', 'three, Hey!', 'ale, Hey!', 'me, Hey!') ],
 [ 'suffix' ],
 [  ]) {
   say qq{'@$words' ==> '@{[lcs(@$words)]}';

}</lang>

Output:
'Sunday Monday Tuesday Wednesday Thursday Friday Saturday' ==> 'day'
'Sondag Maandag Dinsdag Woensdag Donderdag Vrydag Saterdag dag' ==> 'dag'
'2347 9312347 acx5g2347 12.02347' ==> '2347'
'longest common suffix' ==> ''
'one, Hey! three, Hey! ale, Hey! me, Hey!' ==> 'e, Hey!'
'suffix' ==> 'suffix'
'' ==> ''

Phix

Phix allows negative indexes, with -1 as the last element [same as $], and -length(s) the first element of s, so we can just do this: <lang Phix>function longestcommonsuffix(sequence strings)

   string res = ""
   if length(strings) then
       res = strings[1]
       for i=2 to length(strings) do
           string si = strings[i]
           if length(si)<length(res) then
               res = res[-length(si)..$]
           end if
           for j=-1 to -length(res) by -1 do
               if res[j]!=si[j] then
                   res = res[j+1..$]
                   exit
               end if
           end for
           if length(res)=0 then exit end if
       end for
   end if
   return res

end function

sequence tests = {{"baabababc","baabc","bbbabc"},

                 {"baabababc","baabc","bbbazc"},
                 {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"},
                 {"longest", "common", "suffix"},
                 {"suffix"},
                 {""}}

for i=1 to length(tests) do

   printf(1,"%v ==> \"%s\"\n",{tests[i],longestcommonsuffix(tests[i])})

end for</lang>

Output:
{"baabababc","baabc","bbbabc"} ==> "abc"
{"baabababc","baabc","bbbazc"} ==> "c"
{"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"} ==> "day"
{"longest","common","suffix"} ==> ""
{"suffix"} ==> "suffix"
{""} ==> ""

Python

Pending a fuller task statement and some test samples:

Works with: Python version 3

<lang python>Longest common suffix

from itertools import takewhile from functools import reduce


  1. longestCommonSuffix :: [String] -> String

def longestCommonSuffix(xs):

   Longest suffix shared by all
      strings in xs.
   
   def allSame(cs):
       h = cs[0]
       return all(h == c for c in cs[1:])
   def firstCharPrepended(s, cs):
       return cs[0] + s
   return reduce(
       firstCharPrepended,
       takewhile(
           allSame,
           zip(*(reversed(x) for x in xs))
       ),
       
   )


  1. -------------------------- TEST --------------------------
  2. main :: IO ()

def main():

   Test
   samples = [
       [
           "Sunday", "Monday", "Tuesday", "Wednesday",
           "Thursday", "Friday", "Saturday"
       ], [
           "Sondag", "Maandag", "Dinsdag", "Woensdag",
           "Donderdag", "Vrydag", "Saterdag"
       ]
   ]
   for xs in samples:
       print(
           longestCommonSuffix(xs)
       )


  1. MAIN ---

if __name__ == '__main__':

   main()</lang>
Output:
day
dag

Raku

Works with: Rakudo version 2020.07

<lang perl6>sub longest-common-suffix ( *@words ) {

   return  unless +@words;
   my $min = @words».chars.min;
   for 1 .. * {
       return @words[0].substr(* - $min) if $_ > $min;
       next if @words».substr(* - $_).Set == 1;
       return @words[0].substr(* - $_ + 1);
   }

}

say "{$_.raku} - LCS: >{longest-common-suffix $_}<" for

 <Sunday Monday Tuesday Wednesday Thursday Friday Saturday>,
 <Sondag Maandag Dinsdag Woensdag Donderdag Vrydag Saterdag dag>,
 ( 2347, 9312347, 'acx5g2347', 12.02347 ),
 <longest common suffix>,
 ('one, Hey!', 'three, Hey!', 'ale, Hey!', 'me, Hey!'),
 'suffix',
 </lang>
Output:
("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday") - LCS: >day<
("Sondag", "Maandag", "Dinsdag", "Woensdag", "Donderdag", "Vrydag", "Saterdag", "dag") - LCS: >dag<
(2347, 9312347, "acx5g2347", 12.02347) - LCS: >2347<
("longest", "common", "suffix") - LCS: ><
("one, Hey!", "three, Hey!", "ale, Hey!", "me, Hey!") - LCS: >e, Hey!<
"suffix" - LCS: >suffix<
"" - LCS: ><

REXX

Essentially,   this REXX version simply reversed the strings,   and then finds the longest common  prefix. <lang rexx>/*REXX program finds the longest common suffix contained in an array of strings. */ parse arg z; z= space(z) /*obtain optional arguments from the CL*/ if z==|z=="," then z='baabababc baabc bbbabc' /*Not specified? Then use the default.*/ z= space(z); #= words(z) /*#: the number of words in the list. */ say 'There are ' # " words in the list: " z zr= reverse(z) /*reverse Z, find longest common prefix*/ @= word(zr, 1); m= length(@) /*get 1st word in reversed string; len.*/

    do j=2  to #;    x= word(zr, j)             /*obtain a word (string) from the list.*/
    t= compare(@, x)                            /*compare to the "base" word/string.   */
    if t==1          then do;  @=;  leave       /*A mismatch of strings?   Then leave, */
                          end                   /*no sense in comparing anymore strings*/
    if t==0 & @==x   then t= length(@) + 1      /*Both strings equal?  Compute length. */
    if t>=m  then iterate                       /*T ≥ M?  Then it's not longest string.*/
    m= t - 1;        @= left(@, max(0, m) )     /*redefine max length & the base string*/
    end   /*j*/

say /*stick a fork in it, we're all done. */ if m==0 then say 'There is no common suffix.'

        else say 'The longest common suffix is: '   right( word(z, 1), m)</lang>
output   when using the default input:
There are  3  words in the list:  baabababc baabc bbbabc

The longest common suffix is:  abc

Ring

<lang ring> load "stdlib.ring"

pre = ["baabababc","baabc","bbbabc"] len = len(pre) lenList = list(len) sub = list(len)

see "Input:" + nl see pre

for n = 1 to len

   temp = pre[n]
   pre[n] = rever(temp)

next

for n = 1 to len

   lenList[n] = len(pre[n])

next

lenList = sort(lenList) lenMax = lenList[1]

for m = 1 to lenMax

   check = 0 
   sub1 = substr(pre[1],1,m)
   sub2 = substr(pre[2],1,m) 
   sub3 = substr(pre[3],1,m)
   if sub1 = sub2 and sub2 = sub3
      check = 1
   ok
   if check = 1
      longest = m
   ok

next

longPrefix = substr(pre[1],1,longest) longPrefix = rever(longPrefix)

see "Longest common suffix = " + longPrefix + nl

func rever(cstr)

    cStr2 = ""
    for x = len(cStr) to 1 step -1 
        cStr2 = cStr2 + cStr[x]
    next
    return cStr2

</lang>

Output:
Input:
baabababc
baabc
bbbabc
Longest common suffix = abc

Wren

<lang ecmascript>var lcs = Fn.new { |a|

   if (a.count == 0) return ""
   if (a.count == 1) return a[0]
   var minLen = a.reduce(a[0].count) { |min, s| (s.count < min) ? s.count : min }
   if (minLen == 0) return ""
   var res = ""
   for (i in 1..minLen) {
       var suffix = a[0][-i..-1]
       for (e in a.skip(1)) {
           if (!e.endsWith(suffix)) return res
       }
       res = suffix
   }
   return res

}

var tests = [

   ["baabababc","baabc","bbbabc"],
   ["baabababc","baabc","bbbazc"],
   ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
   ["longest", "common", "suffix"],
   ["suffix"],
   [""]

] for (test in tests) System.print("%(test) -> \"%(lcs.call(test))\"")</lang>

Output:
[baabababc, baabc, bbbabc] -> "abc"
[baabababc, baabc, bbbazc] -> "c"
[Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday] -> "day"
[longest, common, suffix] -> ""
[suffix] -> "suffix"
[] -> ""