最长公共子串(最长重复子数组)

· 约 3 分钟阅读 · 次阅读 leetcode

给两个整数数组 nums1 和 nums2 ,返回 两个数组中 公共的 、长度最长的子数组的长度 。


暴力解法

这种解法过于粗暴,无法通过OJ

class Solution:
    def findLength(self, nums1: List[int], nums2: List[int]) -> int:
        ans = 0
        for i in range(len(nums1)):
            for j in range(len(nums2)):
                k = 0
                while i+k < len(nums1) and j+k < len(nums2) and nums1[i+k] == nums2[j+k]:
                    k += 1
                ans = max(ans, k)
        return ans

不太优雅的滑动窗口?

分别使用A数组的每一个数为起点与B数组比较,如果数字相同计数加1 然后交换A B重新比较

class Solution:
    def findLength(self, nums1: List[int], nums2: List[int]) -> int:
        m = len(nums1)
        n = len(nums2)
        ans = 0
        j = 0
        
        for offset in range(m):
            i = offset
            j = 0
            while i < m and j < n:
                count = 0
                while i < m and j < n and nums1[i] == nums2[j]:
                    count += 1
                    i += 1
                    j += 1
                j += 1
                i += 1
                ans = max(ans, count)
        
        for offset in range(n):
            j = offset
            i = 0
            while i < m and j < n:
                count = 0
                # print('i, j, count', i, j, count)
                while i < m and j < n and nums1[i] == nums2[j]:
                    count += 1
                    i += 1
                    j += 1
                j += 1
                i += 1
                ans = max(ans, count)
        
        return ans

滑动窗口

假设已经对齐了两个数组的起点,我们就可以对两个数组进行一趟遍历,求得最长的数组

class Solution:
    def findLength(self, nums1: List[int], nums2: List[int]) -> int:
        def helper(start1, start2, length):
            k = 0
            ans = 0
            for i in range(length):
                if nums1[start1+i] == nums2[start2+i]:
                    k += 1
                else:
                    k = 0
                ans = max(ans, k)
            return ans

        m = len(nums1)
        n = len(nums2)
        ans = 0
        for i in range(m):
            length = min(m-i, n)
            # 小优化,length如果小于ans,则无需比较了
            if length > ans:
                ans = max(ans, helper(i, 0, length))
        for i in range(n):
            length = min(n-i, m)
            if length > ans:
                ans = max(ans, helper(0, i, length))
        return ans
        

动态规划算法

dp[i][j] 表示长度为i,结尾index为i-1的nums1数组与长度为j,结尾index为j-1的nums2数组的最长公共子数组的长度

class Solution:
    def findLength(self, nums1: List[int], nums2: List[int]) -> int:
        m = len(nums1)
        n = len(nums2)
        dp = [[0]*(n+1) for _ in range(m+1)]
        ans = 0
        for i in range(1, m+1):
            for j in range(1, n+1):
                if nums1[i-1] == nums2[j-1]:
                    dp[i][j] = dp[i-1][j-1] + 1
                else:
                    dp[i][j] = 0
                ans = max(dp[i][j], ans)
        return ans
        

动态规划算法优化

发现上述dp其实每次只依赖上一行左上角的值,也就是内层循环。可以优化一下空间复杂度。 但是为了避免左上角的值被覆盖,内层循环需要从右向左计算。

可以将空间复杂度降低为O(n)

class Solution:
    def findLength(self, nums1: List[int], nums2: List[int]) -> int:
        m = len(nums1)
        n = len(nums2)
        dp = [0]*(n+1)
        ans = 0
        for i in range(1, m+1):
            for j in range(n, 0, -1):
                if nums1[i-1] == nums2[j-1]:
                    dp[j] = dp[j-1] + 1
                else:
                    dp[j] = 0
                ans = max(dp[j], ans)
        return ans
        

二分法+hash

TODO

输出子串

TODO