题目描述 对称子字符串
Description Given a string ‘str’ of digits, find length of the longest substring of ‘str’, such that the length of the substring is 2k digits and sum of left k digits is equal to the sum of right k digits.
Input 输入第一行是测试用例的个数,后面每一行表示一个数字组成的字符串,例如:”123123”
Output 输出找到的满足要求的最长子串的长度。例如,给定的例子长度应该是 6。每行对应一个用例的结果。
Sample Input 1 1 1538023
Sample Output 1 4
分析 题目中有几个关键信息,就是子字符串长度为偶数,且子字符串左侧之和与右侧之和相同。 在判断奇偶时可以使用位运算,即与1做与运算,即:
1 2 3 4 5 6 if ( n & 1 == 1 ) { } else { }
题解 首先是通用的获取输入的方法
1 2 3 4 5 6 7 8 9 10 public static void main (String[] args) { Scanner scanner = new Scanner(System.in); int numOfTestCases = Integer.parseInt(scanner.nextLine()); while (numOfTestCases-- > 0 ) { String input = scanner.nextLine(); System.out.println(calLongestLength(input.toCharArray())); } }
解法一:暴力法 遍历所有长度为偶数的子字符串,判断这些字符串的左半部分和右半部分的和是否相同。 以下是暴力法的题解,i表示从字符串第一个字符处开始遍历,第二个for循环中j从i的下一个字符处开始遍历,索引值每次加2,因为题目要求子字符串长度必须为偶数,此时j-i+1就是子字符串的长度了。然后再进行第三层循环,计算子字符串的左半部分的和以及右半部分的和,然后判断左半部分的和与右半部分的和是否相同,最后更新最大的字符串长度值。 这里要注意的是,子字符串左半部分和右半部分索引的取值。举例来说: 原字符串: 1 2 3 4 4 3 2 1 此时假设i=0,j=3,则字符串长度为4,左半部分是1 2,索引为0、1,右半部分是3 4,索引为2 、3,k从0开始遍历到2(k最多取1), 则inputArray[i+k]刚好是inputArray[0]、inputArray[1],即取到1和2,右侧inputArray[i+k+lengthOfCurSubString/2]刚好取到inputArray[2]、inputArray[3]。 当原字符串是奇数也是同理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 private static int calLongestLength (char [] inputArray) { if (inputArray == null || inputArray.length == 0 ) { return 0 ; } int lengthOfInput = inputArray.length; int maxLen = 0 ; for (int i = 0 ; i < lengthOfInput; i++) { for (int j = i + 1 ; j < lengthOfInput; j += 2 ) { int lengthOfCurSubString = j - i + 1 ; int leftSum = 0 ; int rightSum = 0 ; for (int k = 0 ; k < lengthOfCurSubString / 2 ; k++) { leftSum += (inputArray[i + k] - '0' ); rightSum += (inputArray[i + k + lengthOfCurSubString / 2 ] - '0' ); } if (leftSum == rightSum && maxLen < lengthOfCurSubString) { maxLen = lengthOfCurSubString; } } } return maxLen; }
时间复杂度:O(n3)
解法二 截取法 在解法一种,我们遇到了计算连续的子数组和的问题,因此自然会想到使用截取法来优化计算 代码如下所示。 注意,这里的关键点是索引的选取。sumSoFarArray数组用于记录到目前为止数组的和,sumSoFarArray[0] = 0,因此sumSoFarArray[i]表示inputArray[0]+inputArray[1]+…+inputArray[i-1]+inputArray[i],即inputArray中第i个元素之前的和(包括这第i个元素,此处i从1开始取),实际上是等于sumSoFarArray[i],因此当for循环时,索引i是从0开始取的,对应以下代码中,j表示子数组的右侧最后一个元素的位置,那么从第一个元素到j这个元素的和,应该是sumSoFarArray[j+1]。 此外,还有middleIndex,这个表示的是子数组的中间元素的位置,可以发现,它实际上表示的是右半部分的第一个元素 举例来说,还是以上面原数组是1 2 3 4 5 6 7 8 为例,此时i=1,j=4,子数组是2 3 4 5,middleIndex = 3,表示的是4这个元素,是右半部分的第一个元素。而sumSoFarArray[3]表示前三个元素的和,因此右半部分的和为sumSoFarArray[j + 1] - sumSoFarArray[middleIndex],左侧同理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 private static int calLongestLengthByIntercept (char [] inputArray) { if (inputArray == null || inputArray.length == 0 ) { return 0 ; } int lengthOfInput = inputArray.length; int maxLen = 0 ; int [] sumSoFarArray = new int [lengthOfInput + 1 ]; sumSoFarArray[0 ] = 0 ; for (int i = 1 ; i < lengthOfInput + 1 ; i++) { sumSoFarArray[i] = sumSoFarArray[i - 1 ] + (inputArray[i - 1 ] - '0' ); } for (int i = 0 ; i < lengthOfInput; i++) { for (int j = i + 1 ; j < lengthOfInput; j++) { int curSubArrayLength = j - i + 1 ; if ((curSubArrayLength & 1 ) == 1 ) { continue ; } int middleIndex = i + curSubArrayLength / 2 ; if (sumSoFarArray[j + 1 ] - sumSoFarArray[middleIndex] == sumSoFarArray[middleIndex] - sumSoFarArray[i]) { maxLen = Math.max(curSubArrayLength, maxLen); } } } return maxLen; }
解法二的时间复杂度是O(N2),用到了额外的O(N)的空间
解法三 中心扩散法 这个解法的意思是,从字符串的中点出发,同时往左右两侧扩展,计算左右两侧的和,如果相同则更新子字符串的最大值。 代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 private static int calLongestLengthByExpandMid (char [] inputArray) { if (inputArray == null || inputArray.length == 0 ) { return 0 ; } int lengthOfInput = inputArray.length; int maxLen = 0 ; for (int i = 0 ; i < lengthOfInput - 1 ; i++) { int leftIndex = i; int rightIndex = i + 1 ; int leftSum = 0 ; int rightSum = 0 ; while (rightIndex < lengthOfInput && leftIndex >= 0 ) { leftSum += inputArray[leftIndex] - '0' ; rightSum += inputArray[rightIndex] - '0' ; if (leftSum == rightSum) { maxLen = Math.max(maxLen, rightIndex - leftIndex + 1 ); } leftIndex--; rightIndex++; } } return maxLen; }
解法三的时间复杂度是O(N2),空间复杂度为O(1)
相关题目 [1]最长回文子串 [2]字符串回文相关题目