I'm working on modernizing Rosetta Code's infrastructure. Starting with communications. Please accept this time-limited open invite to RC's Slack.. --Michael Mol (talk) 20:59, 30 May 2020 (UTC)

Longest common suffix

From Rosetta Code
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

Write a function to find the longest common suffix string amongst an array of strings.


Other tasks related to string operations:
Metrics
Counting
Remove/replace
Anagrams/Derangements/shuffling
Find/Search/Determine
Formatting
Song lyrics/poems/Mad Libs/phrases
Tokenize
Sequences



ALGOL 68[edit]

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.

# 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 # ;
# get the length of a string #
OP LEN = ( STRING a )INT: ( UPB a + 1 ) - LWB a;
# 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 ;
# 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
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[edit]

Procedural[edit]

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.

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"}) --> ""

Functional[edit]

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

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

AutoHotkey[edit]

Longest_common_suffix(data){
for num, v in StrSplit(data.1)
for i, word in data
if (SubStr(word, 1-num) <> SubStr(data.1, 1-num))
return num=1 ? "" : SubStr(word, 2-num)
return SubStr(word, 1-num)
}
Examples:
MsgBox % ""
. "`n" Longest_common_suffix(["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"])
. "`n" Longest_common_suffix(["throne", "throne"])
. "`n" Longest_common_suffix(["throne", "dungeon"])
. "`n" Longest_common_suffix(["throne", "", "throne"])
. "`n" Longest_common_suffix(["cheese"])
. "`n" Longest_common_suffix([""])
. "`n" Longest_common_suffix(["prefix", "suffix"])
. "`n" Longest_common_suffix(["bar", "foobar"])
return
Output:
day
throne


cheese

fix
bar

BaCon[edit]

FUNCTION Common_Suffix$(data$)
 
LOCAL x, size
LOCAL delim$
 
REPEAT
delim$ = ""
INCR size
 
FOR x = 1 TO AMOUNT(data$)
delim$ = APPEND$(delim$, 0, RIGHT$(TOKEN$(data$, x), size))
NEXT
UNTIL AMOUNT(UNIQ$(delim$)) <> 1
 
RETURN RIGHT$(TOKEN$(data$, 1), size-1)
 
ENDFUNCTION
 
PRINT "The common suffix is: '", Common_Suffix$("baabababc baabc bbbabc"), "'"
PRINT "The common suffix is: '", Common_Suffix$("Monday Tuesday Wednesday Thursday Friday Saturday Sunday"), "'"
PRINT "The common suffix is: '", Common_Suffix$("longest common suffix"), "'"
PRINT "The common suffix is: '", Common_Suffix$("prefix suffix"), "'"
PRINT "The common suffix is: '", Common_Suffix$(""), "'"
Output:
The common suffix is: 'abc'
The common suffix is: 'day'
The common suffix is: ''
The common suffix is: 'fix'
The common suffix is: ''

Delphi[edit]

Library: Types
Translation of: Ring
 
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.
 
 
Output:
Input:
baabababc
baabc
bbbabc

Longest common suffix = abc

Factor[edit]

Works with: Factor version 0.99 2020-07-03
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 .
Output:
"abc"
"c"
""

Go[edit]

Translation of: Wren
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))
}
}
Output:
[baabababc baabc bbbabc] -> "abc"
[baabababc baabc bbbazc] -> "c"
[Sunday Monday Tuesday Wednesday Thursday Friday Saturday] -> "day"
[longest common suffix] -> ""
[suffix] -> "suffix"
[] -> ""

Haskell[edit]

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:

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"
]
]
Output:
day
dag


JavaScript[edit]

(() => {
'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();
})();
Output:
Longest common suffix:

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

Julia[edit]

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([""]))
Output:
abc
c

Perl[edit]

Based on Longest_common_prefix Perl entry.

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)]}';
}
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[edit]

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:

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
Output:
{"baabababc","baabc","bbbabc"} ==> "abc"
{"baabababc","baabc","bbbazc"} ==> "c"
{"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"} ==> "day"
{"longest","common","suffix"} ==> ""
{"suffix"} ==> "suffix"
{""} ==> ""

Python[edit]

Pending a fuller task statement and some test samples:

Works with: Python version 3
'''Longest common suffix'''
 
from itertools import takewhile
from functools import reduce
 
 
# 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))
),
''
)
 
 
# -------------------------- TEST --------------------------
# 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)
)
 
 
# MAIN ---
if __name__ == '__main__':
main()
Output:
day
dag

Raku[edit]

Works with: Rakudo version 2020.07
sub longest-common-suffix ( *@words ) {
return '' unless [email protected]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',
''
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[edit]

Essentially,   this REXX version simply reversed the strings,   and then finds the longest common  prefix.

/*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)
output   when using the default input:
There are  3  words in the list:  baabababc baabc bbbabc

The longest common suffix is:  abc

Ring[edit]

 
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
 
Output:
Input:
baabababc
baabc
bbbabc
Longest common suffix = abc

Wren[edit]

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))\"")
Output:
[baabababc, baabc, bbbabc] -> "abc"
[baabababc, baabc, bbbazc] -> "c"
[Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday] -> "day"
[longest, common, suffix] -> ""
[suffix] -> "suffix"
[] -> ""