1. 回文字符串基础知识
回文字符串是一系列字符,正着读和反着读都是一样的,如“madam”或“racecar”。
在编程中,我们时常需要检测或者处理回文字符串,例如数据验证、dna序列分析等。
1.1 简述回文字符串的定义
回文是指一个字符串忽略标点、大小写和空格,正向和反向都一样。
回文字符串的定义很简单,举例来说:“level”, “deified”, “civic” 和 “radar” 等都是回文字符串。
1.2 回文字符串的性质和应用场景
- 性质:回文字符串的主要性质是它们是对称的,其第一个字符和最后一个字符相同,第二个字符和倒数第二个字符相同,以此类推。
- 应用场景:回文字符串的概念在许多领域都有应用,例如文本编辑器中查找回文单词,生物学中查找特定的dna序列,甚至在某些加密算法中也会使用到回文结构。
2. 查找回文子字符串的方法
2.1. 暴力方法
暴力方法是通过检查所有的子字符串,来确定它们是否为回文。以下是使用java语言的代码示例:
public class bruteforcepalindrome {
public static boolean ispalindrome(string s) {
for (int i = 0, j = s.length() - 1; i < j; i++, j--) {
if (s.charat(i) != s.charat(j)) {
return false;
}
}
return true;
}
public static void findpalindromes(string input) {
int n = input.length();
for (int i = 0; i < n; i++) {
for (int j = i + 1; j <= n; j++) {
string substr = input.substring(i, j);
if (ispalindrome(substr)) {
system.out.println(substr);
}
}
}
}
public static void main(string[] args) {
string s = "ababa";
findpalindromes(s);
}
}
2.2. 中心扩展算法
中心扩展算法从每个可能的中心开始检查可能的最长回文。
以下是java代码示例:
public class centerexpansionpalindrome {
public static void findpalindromes(string input) {
for (int center = 0; center < 2 * input.length() - 1; center++) {
int left = center / 2;
int right = left + center % 2;
while (left >= 0 && right < input.length() && input.charat(left) == input.charat(right)) {
system.out.println(input.substring(left, right + 1));
left--;
right++;
}
}
}
public static void main(string[] args) {
string s = "racecar";
findpalindromes(s);
}
}
2.3. manacher’s algorithm(马拉车算法)
马拉车算法是一种高效的查找最长回文子字符串的方法。
这里是一个java实现:
public class manacheralgorithm {
private static string preprocess(string s) {
stringbuilder sb = new stringbuilder("$#");
for (int i = 0; i < s.length(); i++) {
sb.append(s.charat(i));
sb.append('#');
}
sb.append('@');
return sb.tostring();
}
public static void findpalindromes(string input) {
string t = preprocess(input);
int n = t.length();
int[] p = new int[n];
int c = 0, r = 0;
for (int i = 1; i < n - 1; i++) {
int i_mirror = 2 * c - i;
p[i] = (r > i) ? math.min(r - i, p[i_mirror]) : 0;
while (t.charat(i + 1 + p[i]) == t.charat(i - 1 - p[i])) {
p[i]++;
}
if (i + p[i] > r) {
c = i;
r = i + p[i];
}
}
for (int i = 1; i < n - 1; i++) {
if(p[i] > 0) {
system.out.println(input.substring((i - 1 - p[i]) / 2, (i - 1 + p[i]) / 2));
}
}
}
public static void main(string[] args) {
string s = "abababa";
findpalindromes(s);
}
}
在上述实现中,先对原始字符串进行预处理,在每个字符间插入特殊符号(比如#),这样可以统一处理偶数长度和奇数长度的回文。然后使用manacheralgorithm中的findpalindromes方法寻找回文子字符串。
3. 统计回文子字符串出现次数
3.1. 哈希表的使用
在找到所有回文子字符串后,我们需要统计它们的出现次数。要有效地做到这一点,我们可以使用哈希表(在java中通常是hashmap)来存储每个子字符串及其对应的计数。
下面的java代码示例展示了如何使用哈希表来统计回文子字符串的次数。
import java.util.hashmap;
import java.util.map;
public class palindromefrequency {
public static map<string, integer> countpalindromes(string[] palindromes) {
map<string, integer> palindromecount = new hashmap<>();
for (string palindrome : palindromes) {
palindromecount.put(palindrome, palindromecount.getordefault(palindrome, 0) + 1);
}
return palindromecount;
}
// 假设我们已经有了一个回文子字符串数组
public static void main(string[] args) {
string[] palindromes = {"aba", "level", "ana", "level"};
map<string, integer> count = countpalindromes(palindromes);
for (string key : count.keyset()) {
system.out.println("palindrome: " + key + ", count: " + count.get(key));
}
}
}
3.2. 结果去重和统计实现
为了去除重复的回文子字符串,我们可以在将字符串加入哈希表之前,先检查它是否已经存在。此外,我们也可能需要对回文字符串按照一定的规则(如字母表顺序)进行排序,以便更好地组织和查询。
以下代码示例展示了如果同时考虑去重和统计:
import java.util.treemap;
public class palindromefrequencyanddeduplication {
public static treemap<string, integer> countpalindromes(string input) {
treemap<string, integer> palindromecount = new treemap<>();
// 此处省略查找回文子字符串的代码... (可以使用上面讨论的任何算法)
// 假设我们已经有了一个包含所有回文子字符串的列表
string[] foundpalindromes = {"aba", "level", "ana", "level"};
for (string palindrome : foundpalindromes) {
palindromecount.put(palindrome, palindromecount.getordefault(palindrome, 0) + 1);
}
return palindromecount;
}
public static void main(string[] args) {
string s = "abacdclevelanalevel";
treemap<string, integer> count = countpalindromes(s);
for (map.entry<string, integer> entry : count.entryset()) {
system.out.println("palindrome: " + entry.getkey() + ", count: " + entry.getvalue());
}
}
}
使用treemap而不是hashmap是因为treemap会根据键自然排序,使得输出更易于阅读和检查。
4. 具体实现

4.1. 纯java实现代码示例
现在来实现一个完整的java类,该类可以一次性查找所有的回文子字符串,并统计每个回文子字符串的出现次数。
import java.util.hashmap;
import java.util.map;
public class palindromefinder {
private static boolean ispalindrome(string s) {
for (int i = 0, j = s.length() - 1; i < j; i++, j--) {
if (s.charat(i) != s.charat(j)) {
return false;
}
}
return true;
}
public static map<string, integer> findallpalindromes(string s) {
map<string, integer> palindromeoccurrences = new hashmap<>();
for (int i = 0; i < s.length(); i++) {
for (int j = i + 1; j <= s.length(); j++) {
string substr = s.substring(i, j);
if (ispalindrome(substr)) {
palindromeoccurrences.put(substr, palindromeoccurrences.getordefault(substr, 0) + 1);
}
}
}
return palindromeoccurrences;
}
public static void main(string[] args) {
string s = "abaxabaxabb";
map<string, integer> palindromes = findallpalindromes(s);
for (map.entry<string, integer> entry : palindromes.entryset()) {
system.out.println("palindrome: " + entry.getkey() + ", count: " + entry.getvalue());
}
}
}
4.2. 优化方案和性能分析
尽管上述实现可以工作,但在大型字符串上可能会变得非常慢。
优化可以从两个方面进行:
- 使用更高效的算法,比如manacher’s algorithm,来找回文子串。
- 对记录和统计过程进行优化,比如使用treemap以保持排序,或者其它结构来优化查询和更新操作。
性能方面,我们应该考虑算法的时间复杂度和空间复杂度。原始的暴力搜索算法的时间复杂度是o(n^3),并且我们还需要额外的空间来存储所有的子字符串和计数。优化后的算法,如manacher’s算法,将时间复杂度提高到了o(n),这对于处理长字符串数据来说,提升是非常明显的。
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论