You are given a string text
. You should split it to k substrings (subtext1, subtext2, ..., subtextk)
such that:
subtexti
is a non-empty string.text
(i.e., subtext1 + subtext2 + ... + subtextk == text
).subtexti == subtextk - i + 1
for all valid values of i
(i.e., 1 <= i <= k
).Return the largest possible value of k
.
Example 1:
Input: text = "ghiabcdefhelloadamhelloabcdefghi" Output: 7 Explanation: We can split the string on "(ghi)(abcdef)(hello)(adam)(hello)(abcdef)(ghi)".
Example 2:
Input: text = "merchant" Output: 1 Explanation: We can split the string on "(merchant)".
Example 3:
Input: text = "antaprezatepzapreanta" Output: 11 Explanation: We can split the string on "(a)(nt)(a)(pre)(za)(tep)(za)(pre)(a)(nt)(a)".
Constraints:
1 <= text.length <= 1000
text
consists only of lowercase English characters.Given a string text
, we need to split it into k
substrings such that:
text
.subtext<sub>i</sub> == subtext<sub>k - i + 1</sub>
for all valid i
.The goal is to find the maximum possible value of k
.
This solution employs a greedy approach using two pointers. We iterate from both ends of the string, searching for the shortest identical non-overlapping prefixes and suffixes.
Algorithm:
i
and j
, to the beginning and end of the string, respectively.i ≤ j
:
k
).text[i:i+k] == text[j-k+1:j+1]
):
ans
by 2 (since we found a pair of matching substrings).i
and j
to skip the matched substrings.k
:
ans
by 1 (the remaining part is a single palindrome chunk).ans
.Time Complexity: O(n^2) in the worst case, where n is the length of the string. This happens when we need to check all possible prefix/suffix lengths for each position.
Space Complexity: O(1). We only use a constant amount of extra space.
Code (Python):
class Solution:
def longestDecomposition(self, text: str) -> int:
ans = 0
i, j = 0, len(text) - 1
while i <= j:
k = 1
ok = False
while i + k - 1 < j - k + 1:
if text[i : i + k] == text[j - k + 1 : j + 1]:
ans += 2
i += k
j -= k
ok = True
break
k += 1
if not ok:
ans += 1
break
return ans
Code (Java):
class Solution {
public int longestDecomposition(String text) {
int ans = 0;
for (int i = 0, j = text.length() - 1; i <= j;) {
boolean ok = false;
for (int k = 1; i + k - 1 < j - k + 1; ++k) {
if (check(text, i, j - k + 1, k)) {
ans += 2;
i += k;
j -= k;
ok = true;
break;
}
}
if (!ok) {
++ans;
break;
}
}
return ans;
}
private boolean check(String s, int i, int j, int k) {
while (k-- > 0) {
if (s.charAt(i++) != s.charAt(j++)) {
return false;
}
}
return true;
}
}
Similar code can be written in other languages like C++, Go, and JavaScript following the same logic.
This solution optimizes the string comparison using a rolling hash function. Instead of directly comparing substrings, we compute their hash values and compare the hashes for quicker equality checks.
Algorithm:
Time Complexity: O(n) on average, assuming minimal hash collisions. Hash computation takes linear time.
Space Complexity: O(n) to store the hash values.
Code (Python):
class Solution:
def longestDecomposition(self, text: str) -> int:
def get(l, r):
return (h[r] - h[l - 1] * p[r - l + 1]) % mod
n = len(text)
base = 131
mod = int(1e9) + 7
h = [0] * (n + 10)
p = [1] * (n + 10)
for i, c in enumerate(text):
t = ord(c) - ord('a') + 1
h[i + 1] = (h[i] * base) % mod + t
p[i + 1] = (p[i] * base) % mod
ans = 0
i, j = 0, n - 1
while i <= j:
k = 1
ok = False
while i + k - 1 < j - k + 1:
if get(i + 1, i + k) == get(j - k + 2, j + 1):
ans += 2
i += k
j -= k
ok = True
break
k += 1
if not ok:
ans += 1
break
return ans
Again, similar code can be adapted for other languages. The core idea remains consistent: use a rolling hash for efficient string comparisons. Note that the choice of hash function and its parameters (base, modulo) is crucial for minimizing collisions. This approach trades off a bit of space complexity for a significant improvement in time complexity, making it more suitable for larger input strings.