tags:

views:

445

answers:

3

Hi,

I'm looking for a python library for finding the longest common substring from a set of python strings.

I'have read that it exist to way to solve this problem : - one using suffix trees - the other using dynamic programming.

The method implemented is not important. Otherwise, it is important to have a implementation that can be use for a set of strings and not only two strings

Thanks,

A: 
def common_prefix(strings):
    """ Find the longest string that is a prefix of all the strings.
    """
    if not strings:
        return ''
    prefix = strings[0]
    for s in strings:
        if len(s) < len(prefix):
            prefix = prefix[:len(s)]
        if not prefix:
            return ''
        for i in range(len(prefix)):
            if prefix[i] != s[i]:
                prefix = prefix[:i]
                break
    return prefix

From http://bitbucket.org/ned/cog/src/tip/cogapp/whiteutils.py

Ned Batchelder
The OP asked for any substring, not just the prefix.
Stephen
Thanks, but I was looking for a generic substring implementation, not a prefix one.
Nicolas Noël
Oops, my bad, sorry!
Ned Batchelder
+1  A: 

These paired functions will find the longest common string in any arbitrary array of strings:

def long_substr(data):
    substr = ''
    if len(data) > 1 and len(data[0]) > 0:
        for i in range(len(data[0])):
            for j in range(len(data[0])-i+1):
                if j > len(substr) and is_substr(data[0][i:i+j], data):
                    substr = data[0][i:i+j]
    return substr

def is_substr(find, data):
    if len(data) < 1 and len(find) < 1:
        return False
    for i in range(len(data)):
        if find not in data[i]:
            return False
    return True


print long_substr(['Oh, hello, my friend.',
                   'I prefer Jelly Belly beans.',
                   'When hell freezes over!'])

No doubt the algorithm could be improved and I've not had a lot of exposure to Python, so maybe it could be more efficient syntactically as well, but it should do the job.

EDIT: in-lined the second is_substr function as demonstrated by J.F. Sebastian. Usage remains the same. Note: no change to algorithm.

def long_substr(data):
    substr = ''
    if len(data) > 1 and len(data[0]) > 0:
        for i in range(len(data[0])):
            for j in range(len(data[0])-i+1):
                if j > len(substr) and all(data[0][i:i+j] in x for x in data):
                    substr = data[0][i:i+j]
    return substr

Hope this helps,

Jason.

jtjacques
Your algorithm has O(n1*n1*(n1 + ... + nK)) time complexity, but using suffix tree it can be reduced to Θ(n1 + ... + nK) http://en.wikipedia.org/wiki/Longest_common_substring_problem#Algorithms
J.F. Sebastian
`is_common_substr = lambda s, strings: all(s in x for x in strings)`
J.F. Sebastian
A: 
# this does not increase asymptotical complexity
# but can still waste more time than it saves. TODO: profile
def shortest_of(strings):
    return min(strings, key=len)

def long_substr(strings):
    substr = ""
    if not strings:
        return substr
    reference = shortest_of(strings) #strings[0]
    length = len(reference)
    #find a suitable slice i:j
    for i in xrange(length):
        #only consider strings long at least len(substr) + 1
        for j in xrange(i + len(substr) + 1, length):
            candidate = reference[i:j]  # ↓ is the slice recalculated every time?
            if all(candidate in text for text in strings):
                substr = candidate
    return substr

Disclaimer This adds very little to jtjacques' answer. However, hopefully, this should be more readable and faster and it didn't fit in a comment, hence why I'm posting this in an answer. I'm not satisfied about shortest_of, to be honest.

badp
Please check “functional” version of `shortest_of`.
ΤΖΩΤΖΙΟΥ
@ΤΖΩΤΖΙΟΥ: ...duh, thanks :)
badp
Non fa niente :) Everyone's entitled to getting stuck once in a while.
ΤΖΩΤΖΙΟΥ