Skip to content
LC-3485 Hard LeetCode

3485. Longest Common Prefix of K Strings After Removal

Read the full problem statement on LeetCode.
Difficulty: hard Acceptance: 20% Topics: Array, String, Trie
View full problem on LeetCode
Reference solution (spoiler · python)
# Time:  O(l * nlogn)
# Space: O(n)

# sort, sliding window, prefix sum
class Solution(object):
    def longestCommonPrefix(self, words, k):
        """
        :type words: List[str]
        :type k: int
        :rtype: List[int]
        """
        idxs = range(len(words))
        idxs.sort(key=lambda x: words[x])
        def longest_common_prefix(k):
            lcp = [0]*len(words)
            for i in xrange(len(words)-(k-1)):
                left = words[idxs[i]]
                right = words[idxs[i+(k-1)]]
                l = min(len(left), len(right))
                lcp[i] = next((j for j in xrange(l) if left[j] != right[j]), l)
            return lcp
        
        lcp = longest_common_prefix(k)
        prefix = [0]*len(words)
        prefix[0] = lcp[0]
        for i in xrange(len(prefix)-1):
            prefix[i+1] = max(prefix[i], lcp[i+1])
        suffix = [0]*len(words)
        suffix[-1] = lcp[-1]
        for i in reversed(xrange(len(suffix)-1)):
            suffix[i] = max(suffix[i+1], lcp[i])
        result = [0]*len(words)
        mx = max(longest_common_prefix(k+1))
        for i in xrange(len(words)):
            idx = idxs[i]
            mx1 = prefix[i-k] if i-k >= 0 else 0
            mx2 = suffix[i+1] if i+1 < len(words) else 0
            result[idx] = max(mx, mx1, mx2)
        return result
        

# Time:  O(n * l)
# Space: O(t)
# trie
class Solution2(object):
    def longestCommonPrefix(self, words, k):
        """
        :type words: List[str]
        :type k: int
        :rtype: List[int]
        """
        class Trie(object):
            def __init__(self):
                self.__root = self.__new_node()
            
            def __new_node(self):
                return {"cnt":0, "max":0}

            def update(self, w, d, k):
                path = [None]*(len(w)+1)
                path[0] = curr = self.__root
                for i, x in enumerate(w, 1):
                    if x not in curr:
                        curr[x] = self.__new_node()
                    path[i] = curr = curr[x]
                for i in reversed(xrange(len(path))):
                    curr = path[i]
                    curr["cnt"] += d
                    curr["max"] = i if curr["cnt"] >= k else 0
                    for x in curr.iterkeys():
                        if len(x) == 1:
                            curr["max"] = max(curr["max"], curr[x]["max"])

            def query(self):
                return self.__root["max"]
        

        trie = Trie()
        for w in words:
            trie.update(w, +1, k)
        result = [0]*len(words)
        for i in xrange(len(words)):
            trie.update(words[i], -1, k)
            result[i] = trie.query()
            trie.update(words[i], +1, k)
        return result


# Time:  O(n * l)
# Space: O(t)
# trie
class Solution_TLE(object):
    def longestCommonPrefix(self, words, k):
        """
        :type words: List[str]
        :type k: int
        :rtype: List[int]
        """
        class Trie(object):
            def __init__(self):
                self.__nodes = []
                self.__cnt = []
                self.__mx = []
                self.__new_node()
            
            def __new_node(self):
                self.__nodes.append([-1]*26)
                self.__cnt.append(0)
                self.__mx.append(0)
                return len(self.__nodes)-1

            def update(self, w, d, k):
                path = [-1]*(len(w)+1)
                path[0] = curr = 0
                for i, c in enumerate(w, 1):
                    x = ord(c)-ord('a')
                    if self.__nodes[curr][x] == -1:
                        self.__nodes[curr][x] = self.__new_node()
                    path[i] = curr = self.__nodes[curr][x]
                for i in reversed(xrange(len(path))):
                    curr = path[i]
                    self.__cnt[curr] += d
                    self.__mx[curr] = i if self.__cnt[curr] >= k else 0
                    for x in xrange(len(self.__nodes[curr])):
                        if self.__nodes[curr][x] != -1:
                            self.__mx[curr]= max(self.__mx[curr], self.__mx[self.__nodes[curr][x]])

            def query(self):
                return self.__mx[0]
        

        result = [0]*len(words)
        trie = Trie()
        for w in words:
            trie.update(w, +1, k)
        for i in xrange(len(words)):
            trie.update(words[i], -1, k)
            result[i] = trie.query()
            trie.update(words[i], +1, k)
        return result

Solution from kamyu104/LeetCode-Solutions · MIT