对称子字符串
又名:对称子字符串的最大长度
问题地址
题目描述
题目解析
最长的偶数长度子串,使得上半部分和下半部分的总和相同
给定一个由数字组成的字符串“str”,找出“str”的最长子串的长度,使子串的长度为2k位,左k位的和等于右k位的和。
思路解析
-
顺序遍历,指定i,j并统计求和
# 1538023 # 1 5 # 15 38 # 153 802 # 5 3 # 53 80 # 538 023 ... # 2 3 # 这种方法前一半数组可以使用累加的办法求和,但对于后一半数组是没有办法累加求和的,要额外进行求和操作,因此复杂度是O(n^3)
-
从中间向两边遍历,是第一种方法的改进,这样做的好处是简化了求和.
这种方法是最佳的,又简单有好记,时间复杂度O(N2),空间复杂度O(1)
# 1538023 # 1 5 # 5 3 # 15 38 # 3 8 # 53 80 # 153 802 ... # 2 3 # 这种方法前后两半数组都可以使用累加的方法求和,因此因此复杂度是O(n^2)
-
动态规划
对于这道题,我们很自然的想到先算出来所有字符子串的和,再通过比较相邻两个子串的和是否相等来统计,既然需要计算子串的和,并且子串的和可以通过某种方式累计去求,这就给我们用动态规划解供了了一定的思路
如何创建dp数组?
- i,j分别代表字符串的其实位置和终止位置,因此j>=i
- dp [i] [j]代表以i开头的,到j的字符串的和,
- 最终整个dp数组就存放了所有子串的和
如何更新dp数组?
第一个字符到第二个字符的和等于第一个字符的和加上第二个字符的和,即:sum(1,2)=sum(1)+sum(2)
同理有sum(2,3) =sum(2)+sum(3),不难发现这是一个斜着更新的矩阵
如何判断是否有满足条件的串?
接上,当计算:sum(1,2)时,会用到sum(1)与sum(2)的值,若sum(1)=sum(2)则满足条件并记录
可以达到O(N2)复杂度,但要用空间去存储数组
例子
eg:153803 初始矩阵: 更新一次后的矩阵 第一次全都更新完的矩阵 [[1 0 0 0 0 0] [[1 6 0 0 0 0] [[ 1 6 0 0 0 0] [0 5 0 0 0 0] [0 5 0 0 0 0] [ 0 5 8 0 0 0] [0 0 3 0 0 0] [0 0 3 0 0 0] [ 0 0 3 11 0 0] [0 0 0 8 0 0] [0 0 0 8 0 0] [ 0 0 0 8 8 0] [0 0 0 0 0 0] [0 0 0 0 0 0] [ 0 0 0 0 0 3] [0 0 0 0 0 3]] [0 0 0 0 0 3]] [ 0 0 0 0 0 3]] 全部更新完的矩阵 [[ 1 6 9 17 17 20] [ 0 5 8 16 16 19] [ 0 0 3 11 11 14] [ 0 0 0 8 8 11] [ 0 0 0 0 0 3] [ 0 0 0 0 0 3]]
代码实现
-
思路1的代码实现:
def findLength(str): # init params max_l = 0 # 结果值 num_arr = [int(x) for x in str] # 将字符串转为数组,便于遍历 # 1538023 n = len(str) # 记录数组长度 # core # 依次遍历字符串,i为字符串左端点下标 for i in range(n): k = (n - i) // 2 # 因为k值一定满足2k <= len(str),所以字符串最长为k l_end = k + i for j in range(i, l_end): # 对于字符串的左端点来说,本次遍历的起点是i,终点是k+i, # 遍历的数组下标 left_sum = sum(num_arr[i:j + 1]) # 对左边的数组求和 temp_r = 2 * (j + 1) - i # (j+1) + [(j+1)- i] right_sum = sum(num_arr[j + 1: temp_r]) # 对右边的数组求和 if left_sum == right_sum: # 和相等则记录结果 max_l = max(max_l, 2 * (j + 1 - i)) return max_l if __name__ == '__main__': n = int(input().strip()) for i in range(n): str = input().strip() print(findLength(str))
-
思路2的代码实现
def findLength(str): max_l = 0 # 结果值 num_arr = [int(x) for x in str] # 将字符串转为数组,便于遍历 n = len(str) # 记录数组长度 for i in range(n - 1): left_sum = 0 #用于累和 right_sum = 0 r = i + 1 # 右坐标 l = i # 左坐标 while l >= 0 and r < n: #向左右两端延伸,到头停止 left_sum += num_arr[l] #累和 right_sum += num_arr[r] if left_sum == right_sum: # 和相等则记录结果 max_l = max(max_l, r - l + 1) l -= 1 r += 1 return max_l if __name__ == '__main__': n = int(input().strip()) for i in range(n): str = input().strip() print(findLength(str))
-
思路3的代码实现
注意: 其中Sum[i][j] = (Sum[i][j - k] +Sum[j - k + 1][j]) 实际上就是把从i到j的串拆成i到j-k与j-k+1到j的两个子串并求和即可 length 就是i到j k =length//2 这就保证了若length是偶数,则i到j - k等于j - k + 1到j一样大,即子串等长
# Python3 code that uses Dynamic Programming # to find length of the longest even substring # with same sum of digits in left and right half def findLength(string): n = len(string) maxlen = 0 # Initialize result # A 2D table where sum[i][j] stores # sum of digits from str[i] to str[j]. # Only filled entries are the entries # where j >= i import numpy as np Sum = [[0 for x in range(n)] for y in range(n)] Sum = np.array(Sum) # Fill the diagonal values for # substrings of length 1 for i in range(0, n): Sum[i][i] = int(string[i]) # Fill entries for substrings # of length 2 to n for length in range(2, n + 1): # Pick i and j for current substring for i in range(0, n - length + 1): j = i + length - 1 k = length // 2 # Calculate value of sum[i][j] Sum[i][j] = (Sum[i][j - k] + Sum[j - k + 1][j]) print(Sum) print(i, j, "=", i, j - k, "+", j - k + 1, j) # Update result if 'len' is even, # left and right sums are same and # len is more than maxlen if (length % 2 == 0 and Sum[i][j - k] == Sum[(j - k + 1)][j] and length > maxlen): maxlen = length return maxlen # Driver Code if __name__ == "__main__": string = "153803" print("Length of the substring is", findLength(string)) # This code is contributed # by Rituraj Jain