接着上篇的字符串拆分字符组成回文字符串,这次是不能拆分求最长回文子串。之前在解这个问题的时候,就是硬匹配,还遇到奇偶问题情况,然后看了一点Manacher算法,开始没搞懂,不过用加“#”巧妙解决奇偶情况,最后看懂了,循环的时间复杂度能降到O(n),还是很不错的。
最初的那种分情况讨论的代码就不贴了,贴上加“#”硬匹配的代码和Manacher算法考虑三种情况(i和max比较的两大类)的改进算法。
硬匹配
import java.util.Scanner;
public class Manacher {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String sInput = sc.nextLine();
char[] cInput = sInput.toCharArray();
int n = cInput.length;
char mp[] = new char[2 * n + 1];// 存储加‘#’的字符数组
mp[2 * n] = '#';
int i;
for (i = 0; i < n; i++) {
mp[2 * i] = '#';
mp[2 * i + 1] = cInput[i];
}
int p[] = new int[2 * n + 1];// 存储有效回文长度
p[0] = 0;
p[2 * n] = 0;
p[1] = 1;
p[2 * n - 1] = 1;
int r;// 左右延展半径
int d;// 实际延展字符半径
int maxId = 1;// 存储最长回文字符子串中心
int maxR = 1;// 存储最长回文字符子串的左右延展半径
int maxNum = 1;// 存储最长回文字符子串的有效回文长度
for (i = 2; i < 2 * n - 1; i++) {
r = 1;
d = 0;
while ((i - r >= 0) && (i + r <= 2 * n) && (mp[i - r] == mp[i + r])) {
if (mp[i - r] != '#')
d++;
r++;
}
if (mp[i] == '#')
p[i] = 2 * d;
else
p[i] = 2 * d + 1;
if (maxNum < p[i]) {
maxId = i;
maxR = r - 1;
maxNum = p[i];
}
}
StringBuffer out = new StringBuffer();
for (i = maxId - maxR; i < maxId + maxR; i++) {
if (mp[i] != '#')
out.append(mp[i]);
}
System.out.println("最大回文子串长度:" + maxNum + "\n回文字符串内容为:"
+ out.toString());
}
}
Manacher改进后
import java.util.Scanner;
public class Manacher {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String sInput = sc.nextLine();
char[] cInput = sInput.toCharArray();
int n = cInput.length;
char mp[] = new char[2 * n + 1];// 存储加‘#’的字符数组
mp[2 * n] = '#';
int i;
for (i = 0; i < n; i++) {
mp[2 * i] = '#';
mp[2 * i + 1] = cInput[i];
}
int p[] = new int[2 * n + 1];// 存储延展长度
p[0] = 1;
p[2 * n] = 1;
int id = 0;
int max = 0;
int maxl = 0;// 存储p[]中最大值
int maxid = 0;// 存储p[]中最大值的位置
for (i = 1; i < 2 * n; i++) {// 更新p[]数组
if (i < max) {
p[i] = Math.min(p[2 * id - i], max - i);
} else {
p[i] = 1;
}
while ((i - p[i] >= 0) && (i + p[i] < 2 * n + 1)
&& (mp[i - p[i]] == mp[i + p[i]])) {
p[i]++;
}
if (i + p[i] > max) {
max = i + p[i];
id = i;
}
if (maxl < p[i]) {
maxl = p[i];
maxid = i;
}
}
// p[i]延展长度包括i本身,所以实际半径需要减一
// 实际半径其实也是回文串在原字符串中的长度
int r = maxl - 1;
StringBuffer out = new StringBuffer();
for (i = maxid - r; i < maxid + r; i++) {
if (mp[i] != '#') {
out.append(mp[i]);
}
}
System.out.println("最大回文子串长度:" + (maxl - 1) + "\n回文字符串内容为:"
+ out.toString());
}
}
仅供参考,多多交流。