1. Overview
1.概述
In this quick tutorial, we’ll go through different approaches to finding all substrings within a given string that are palindromes. We’ll also note the time complexity of each approach.
在这个快速教程中,我们将通过不同的方法来寻找一个给定字符串中所有属于重音的子串。我们还将指出每种方法的时间复杂性。
2. Brute Force Approach
2.蛮力方法
In this approach, we’ll simply iterate over the input string to find all the substrings. At the same time, we’ll check whether the substring is a palindrome or not:
在这种方法中,我们将简单地遍历输入的字符串,找到所有的子串。同时,我们将检查子串是否为回文。
public Set<String> findAllPalindromesUsingBruteForceApproach(String input) {
Set<String> palindromes = new HashSet<>();
for (int i = 0; i < input.length(); i++) {
for (int j = i + 1; j <= input.length(); j++) {
if (isPalindrome(input.substring(i, j))) {
palindromes.add(input.substring(i, j));
}
}
}
return palindromes;
}
In the example above, we just compare the substring to its reverse to see if it’s a palindrome:
在上面的例子中,我们只是将子串与它的反面进行比较,看看它是否是一个回文。
private boolean isPalindrome(String input) {
StringBuilder plain = new StringBuilder(input);
StringBuilder reverse = plain.reverse();
return (reverse.toString()).equals(input);
}
Of course, we can easily choose from several other approaches.
当然,我们可以很容易地从其他几种方法中选择。
The time complexity of this approach is O(n^3). While this may be acceptable for small input strings, we’ll need a more efficient approach if we’re checking for palindromes in large volumes of text.
这种方法的时间复杂度是O(n^3)。虽然这对于小的输入字符串来说可能是可以接受的,但是如果我们要检查大量文本中的重音,我们将需要一种更有效的方法。
3. Centralization Approach
3.集中化的方法
The idea in the centralization approach is to consider each character as the pivot and expand in both directions to find palindromes.
集中化方法的思路是:将每个字符视为枢轴,并向两个方向扩展,以寻找调和词。
We’ll only expand if the characters on the left and right side match, qualifying the string to be a palindrome. Otherwise, we continue to the next character.
只有当左右两边的字符匹配时,我们才会展开,以限定该字符串是一个回文。否则,我们将继续扩展到下一个字符。
Let’s see a quick demonstration wherein we’ll consider each character as the center of a palindrome:
让我们看一个简单的演示,我们将把每个字符看作是一个回文体的中心。
public Set<String> findAllPalindromesUsingCenter(String input) {
Set<String> palindromes = new HashSet<>();
for (int i = 0; i < input.length(); i++) {
palindromes.addAll(findPalindromes(input, i, i + 1));
palindromes.addAll(findPalindromes(input, i, i));
}
return palindromes;
}
Within the loop above, we expand in both directions to get the set of all palindromes centered at each position. We’ll find both even and odd length palindromes by calling the method findPalindromes twice in the loop:
在上面的循环中,我们向两个方向扩展,以获得以每个位置为中心的所有回文的集合。我们将通过在循环中两次调用findPalindromes方法来找到偶数和奇数长度的宫廷体:。
private Set<String> findPalindromes(String input, int low, int high) {
Set<String> result = new HashSet<>();
while (low >= 0 && high < input.length() && input.charAt(low) == input.charAt(high)) {
result.add(input.substring(low, high + 1));
low--;
high++;
}
return result;
}
The time complexity of this approach is O(n^2). This is an improvement over our brute-force approach, but we can do even better, as we’ll see in the next section.
这种方法的时间复杂度是O(n^2)。这比我们的暴力方法有所改进,但我们可以做得更好,我们将在下一节看到。
4. Manacher’s Algorithm
4.马纳赫的算法
Manacher’s algorithm finds the longest palindromic substring in linear time. We’ll use this algorithm to find all substrings that are palindromes.
Manacher的算法 在线性时间内找到最长的宫格子串。我们将用这个算法来寻找所有的子串,这些子串都是调弦词。
Before we dive into the algorithm, we’ll initialize a few variables.
在我们深入研究该算法之前,我们将初始化几个变量。
First, we’ll guard the input string with a boundary character at the beginning and end before converting the resulting string to a character array:
首先,我们将在输入字符串的开头和结尾处用一个边界字符进行保护,然后将得到的字符串转换为一个字符数组。
String formattedInput = "@" + input + "#";
char inputCharArr[] = formattedInput.toCharArray();
Then, we’ll use a two-dimensional array radius with two rows — one to store the lengths of odd-length palindromes, and the other to store lengths of even-length palindromes:
然后,我们将使用一个二维数组radius,其中有两行–一行用于存储奇数长度的宫格的长度,另一行用于存储偶数长度的宫格的长度。
int radius[][] = new int[2][input.length() + 1];
Next, we’ll iterate over the input array to find the length of the palindrome centered at position i and store this length in radius[][]:
接下来,我们将遍历输入数组,找出以位置i 为中心的宫格的长度,并将这个长度存储在 radius[][]中。
Set<String> palindromes = new HashSet<>();
int max;
for (int j = 0; j <= 1; j++) {
radius[j][0] = max = 0;
int i = 1;
while (i <= input.length()) {
palindromes.add(Character.toString(inputCharArr[i]));
while (inputCharArr[i - max - 1] == inputCharArr[i + j + max])
max++;
radius[j][i] = max;
int k = 1;
while ((radius[j][i - k] != max - k) && (k < max)) {
radius[j][i + k] = Math.min(radius[j][i - k], max - k);
k++;
}
max = Math.max(max - k, 0);
i += k;
}
}
Finally, we’ll traverse through the array radius[][] to calculate the palindromic substrings centered at each position:
最后,我们将遍历数组radius[][] 来计算以每个位置为中心的复数子串。
for (int i = 1; i <= input.length(); i++) {
for (int j = 0; j <= 1; j++) {
for (max = radius[j][i]; max > 0; max--) {
palindromes.add(input.substring(i - max - 1, max + j + i - 1));
}
}
}
The time complexity of this approach is O(n).
这种方法的时间复杂度为O(n)。
5. Conclusion
5.总结
In this quick article, we discussed the time complexities of different approaches to finding substrings that are palindromes.
在这篇快速文章中,我们讨论了不同方法的时间复杂性,以寻找属于调和词的子串。
As always, the full source code of the examples is available over on GitHub.
一如既往,这些示例的完整源代码可在GitHub上获得over。