You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

133 lines
3.4 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

package class_2022_05_1_week;
// 来自optiver
// 给定一个字符串str和一个正数k
// 你可以随意的划分str成多个子串
// 目的是找到在某一种划分方案中,有尽可能多的回文子串,长度>=k并且没有重合
// 返回有几个回文子串
// 测试链接 : https://leetcode.cn/problems/maximum-number-of-non-overlapping-palindrome-substrings/
public class Code05_PalindromeStringNoLessKLenNoOverlapingMaxParts {
// 暴力尝试
// 为了测试
// 可以改成动态规划,但不是最优解
public static int maxPalindromes1(String s, int k) {
if (s == null || s.length() == 0) {
return 0;
}
char[] str = s.toCharArray();
return process1(str, 0, k);
}
public static int process1(char[] str, int index, int k) {
if (str.length - index < k) {
return 0;
}
int ans = process1(str, index + 1, k);
for (int i = index + k - 1; i < str.length; i++) {
if (isPalindrome(str, index, i)) {
ans = Math.max(ans, 1 + process1(str, i + 1, k));
}
}
return ans;
}
public static boolean isPalindrome(char[] str, int L, int R) {
while (L < R) {
if (str[L++] != str[R--]) {
return false;
}
}
return true;
}
// 最优解
// 时间复杂度O(N)
public static int maxPalindromes2(String s, int k) {
if (s == null || s.length() == 0) {
return 0;
}
char[] str = manacherString(s);
int[] p = new int[str.length];
int ans = 0;
int next = 0;
// k == 5 回文串长度要 >= 5
// next == 0
// 0.... 8 第一块!
// next -> 9
// 9.....17 第二块!
// next -> 18
// 18....23 第三块
// next一直到最后!
while ((next = manacherFind(str, p, next, k)) != -1) {
next = str[next] == '#' ? next : (next + 1);
ans++;
}
return ans;
}
public static char[] manacherString(String s) {
char[] str = s.toCharArray();
char[] ans = new char[s.length() * 2 + 1];
int index = 0;
for (int i = 0; i != ans.length; i++) {
ans[i] = (i & 1) == 0 ? '#' : str[index++];
}
return ans;
}
// s[l...]字符串只在这个范围上且s[l]一定是'#'
// 从下标l开始之前都不算一旦有某个中心回文半径>k马上返回右边界
public static int manacherFind(char[] s, int[] p, int l, int k) {
int c = l - 1;
int r = l - 1;
int n = s.length;
for (int i = l; i < s.length; i++) {
p[i] = r > i ? Math.min(p[2 * c - i], r - i) : 1;
while (i + p[i] < n && i - p[i] > l - 1 && s[i + p[i]] == s[i - p[i]]) {
if (++p[i] > k) {
return i + k;
}
}
if (i + p[i] > r) {
r = i + p[i];
c = i;
}
}
return -1;
}
// 为了测试
public static String randomString(int n, int r) {
char[] str = new char[(int) (Math.random() * n)];
for (int i = 0; i < str.length; i++) {
str[i] = (char) ((int) (Math.random() * r) + 'a');
}
return String.valueOf(str);
}
// 为了测试
public static void main(String[] args) {
int n = 20;
int r = 3;
int testTime = 50000;
System.out.println("测试开始");
for (int i = 0; i < testTime; i++) {
String str = randomString(n, r);
int k = (int) (Math.random() * str.length()) + 1;
int ans1 = maxPalindromes1(str, k);
int ans2 = maxPalindromes2(str, k);
if (ans1 != ans2) {
System.out.println(str);
System.out.println(k);
System.out.println(ans1);
System.out.println(ans2);
System.out.println("出错了!");
break;
}
}
System.out.println("测试结束");
}
}