0

I'm trying to write a function to return the longest common prefix from a series of strings. Using a debugger, saw that my function reaches the longest common prefix correctly, but then when it reaches the statement to return, it begins reverting to earlier stages of the algorithm.

For test case strs = ["flower","flow","flight"]

The output variable holds the following values:-

f > fl > f

instead of returning fl.

Any help would be appreciated, because I don't really know how to Google for this one. Thank you.

class Solution(object):
def longestCommonPrefix(self, strs, output = ''):
    #return true if all chars in string are the same
    def same(s):
        return s == len(s) * s[0]
    
    #return new list of strings with first char removed from each string
    def slicer(list_, list_2 = []):
        for string in list_:
            string1 = string[1:]
            list_2.append(string1)
        return list_2
    
    #return string containing first char from each string
    def puller(list_):
        s = ''
        for string in list_:
            s += string[0]
        return s
    
    #pull first character from each string
    s = puller(strs)
    
    #if they are the same
    #add one char to output
    #run again on sliced list
    if same(s):
        output += s[0]
        self.longestCommonPrefix(slicer(strs), output)            
    
    return output
  • `longestCommonPrefix` calls itself. Therefore when it returns, it might return to a previous call of itself. This is called [recursion](https://stackoverflow.com/questions/3021/what-is-recursion-and-when-should-i-use-it). – Random Davis Feb 01 '21 at 22:48
  • `slicer` has a mutable argument. This means the function will return the same list on each call (i.e. `list_2` wll include all the elements from previous calls), unless you override the default. – ekhumoro Feb 01 '21 at 22:57
  • Just to be more clear...your recursive calls will not have any affect on the value returned by the initial call to `longestCommonPrefix` since they have no affect on the function's `output` variable, which is what is returned. If you take out the second to last line in your code that calls your function recursively, the resulting return value will not change. – CryptoFool Feb 01 '21 at 22:57
  • @ekhumoro - you might be right in that even if calling `longestCommonPrefix` recursively could affect the final result, the OP wouldn't see the correct behavior. But it doesn't matter what's `slicer` does, as there's no way that its behavior can affect the final result of the initial call to `longestCommonPrefix` anyway. `slicer` is called only to affect what is passed to the recursive call to `longestCommonPrefix`. By the time that call is made, the value of `output`, and therefore the result of the initial call to `longestCommonPrefix` is already determined. – CryptoFool Feb 01 '21 at 23:01
  • @Steve I didn't say it was the only bug... – ekhumoro Feb 01 '21 at 23:05
  • I'm surprised that you accepted an answer that means you don't need to write any code. I would have thought that the whole point would be that you write a solution of your own instead of using a library. If I'm wrong, then great! But if I'm right, see my answer, in which I was able to get your code to return the right result with a few added characters. – CryptoFool Feb 01 '21 at 23:19

4 Answers4

1

This can be handled with os.path.commonprefix.

>>> import os
>>> strs = ["flower","flow","flight"]
>>> os.path.commonprefix(strs)
'fl'
mdeverna
  • 322
  • 3
  • 9
  • 1
    I can't have a fundamental problem with this answer, I guess, but I do find it striking how the answer to a "Why isn't my code doing what I expect?" question ends up with the accepted answer being: "throw all that code away and replace it with this one-liner from a library.". Doesn't do the question itself much justice, can't we agree? But I shouldn't be surprised I guess that the quality of the Q/A entry itself isn't really on the radar of most askers or contributors. – CryptoFool Feb 02 '21 at 00:12
  • @Steve, I think it was pretty clear what OP was trying to accomplish here. In particular, the final line - "Any help would be appreciated, because I don't really know how to Google for this one. Thank you." - makes it clear that OP is just looking for a solution to the problem. Also, I would say it's generally good programming advice to not reinvent the wheel... You can argue the form of the question could be clearer but maybe a better solution is just to be a nice person and realize the point of this site is to help people? – mdeverna Feb 02 '21 at 00:43
0

It doesn't "revert". longestCommonPrefix potentially calls itself - what you're seeing is simply the call-stack unwinding, and flow of execution is returning to the calling code (the line that invoked the call to longestCommonPrefix from which you are returning).

That being said, there's really no need to implement a recursive solution in the first place. I would suggest something like:

def get_common_prefix(strings):

    def get_next_prefix_char():
        for chars in zip(*strings):
            if len(set(chars)) != 1:
                break
            yield chars[0]

    return "".join(get_next_prefix_char())


print(get_common_prefix(["hello", "hey"]))
Paul M.
  • 10,481
  • 2
  • 9
  • 15
0

You are looking at the behavior...the final result...of recursive calls to your method. However, the recursive calls don't do anything to affect the result of the initial execution of the method. If we look at the few lines that matter at the end of your method:

if same(s):
    output += s[0]
    self.longestCommonPrefix(slicer(strs), output)            

return output

The problem here is that since output is immutable, its value won't be changed by calling longestCommonPrefix recursively. So from the standpoint of the outermost call to longestCommonPrefix, the result it will return is determined only by if same(s) is true or false. If it is true it will return s[0], otherwise it will return ''.

The easiest way to fix this behavior and have your recursive call affect the result of the prior call to the method would be to have its return value become the value of output, like this:

if same(s):
    output += s[0]
    output = self.longestCommonPrefix(slicer(strs), output)            

return output

This is a common code pattern when using recursion. Just this change does seem to give you the result you expect! I haven't analyzed your whole algorithm, so I don't know if it becomes "correct" with just this change.

CryptoFool
  • 21,719
  • 5
  • 26
  • 44
0

Can you try this? I

class Solution(object):
def longestCommonPrefix(self, strs, output = ''):
    #return true if all chars in string are the same
    def same(s):
        return s == len(s) * s[0]
    
    #return new list of strings with first char removed from each string
    def slicer(list_, list_2 = []):
        for string in list_:
            string1 = string[1:]
            list_2.append(string1)
        return list_2
    
    #return string containing first char from each string
    def puller(list_):
        s = ''
        for string in list_:
            s += string[0]
        return s
    
    #pull first character from each string
    s = puller(strs)
    
    # Can you Try this revision? 
    # I think the problem is that your new version of output is being lost when the fourth called function returns to the third and the third returns to the second, etc...
    # You need to calculate a new output value before you call recursively, that is true, but you also need a way to 'store' that output when that recursively called function 'returns'.  Right now it disappears, I believe.
    if same(s):  
        output += s[0]
        output = self.longestCommonPrefix(slicer(strs), output)            

    return output






Rasputin
  • 122
  • 10