给你一个由小写字母组成的字符串 s,和一个整数 k。
请你按下面的要求分割字符串:
首先,你可以将 s 中的部分字符修改为其他的小写英文字母。
接着,你需要把 s 分割成 k 个非空且不相交的子串,并且每个子串都是回文串。
请返回以这种方式分割字符串所需修改的最少字符数。
示例 1:
输入:s = "abc", k = 2
输出:1
解释:你可以把字符串分割成 "ab" 和 "c",并修改 "ab" 中的 1 个字符,将它变成回文串。
示例 2:
输入:s = "aabbc", k = 3
输出:0
解释:你可以把字符串分割成 "aa"、"bb" 和 "c",它们都是回文串。
示例 3:
输入:s = "leetcode", k = 8
输出:0
提示:
1 <= k <= s.length <= 100
s 中只含有小写英文字母。
class Solution {
public int palindromePartition(String s, int k) {
List<List<String>> lists = new ArrayList<>();
if (k == 1) {
List<String> dd = new ArrayList<>();
dd.add(s);
lists.add(dd);
} else {
lists = splitString(s,k-1);
}
int min = s.length();
for (List<String> list : lists) {
int c = 0;
for (String s1 : list) {
for (int i = 0;i<s1.length()/2;i++) {
if (s1.charAt(i) != s1.charAt(s1.length() - i - 1)) {
c++;
}
}
}
min = Math.min(min,c);
}
return min;
}
public List<List<String>> splitString(String s, int m) {
List<List<String>> lists = new ArrayList<>();
if (m == 1) {//当m次数为1时不再分割,返回分割的可能
for (int i = 0;i < s.length() - 1; i++) {
List<String> list = new ArrayList<>();
list.add(s.substring(0,i+1));
list.add(s.substring(i+1,s.length()));
lists.add(list);
}
return lists;
} else {//分割后段的长度必须大于等于分割次数
for (int i = s.length() - m;i > 0;i--) {
List<List<String>> lastList = splitString(s.substring(i,s.length()),m-1);//递归
for (List<String> strings : lastList) {
List<String> list = new ArrayList<>();
list.add(s.substring(0,i));
list.addAll(strings);
lists.add(list);
}
}
}
return lists;
}
}
要点
1.采用暴力递归法,先获得一个字符串s分割成k个字符串可能产生的所有情况存入链表
2.再遍历每个链表 计算每种可能需要修改字符的次数
3.但是字符过长,分割k的次数过大时时间消耗过长,不建议采用这个方法
第二次改进
private Map<String, Map<Integer, Integer>> memory = new HashMap<>();
public int palindromePartition(String s, int k) {
int min = s.length();
if (k == 1) {
min = toS(s);
} else {
min = splitString(s,k-1);
}
return min;
}
public int splitString(String s, int m) {
List<List<String>> lists = new ArrayList<>();
int min = s.length(); if (memory.get(s) != null) { if (memory.get(s).get(m) != null) { min = memory.get(s).get(m); } }
if (m == 1) {//当m次数为1时不再分割,返回s
for (int i = 0;i < s.length() - 1; i++) {
List<String> list = new ArrayList<>();
list.add(s.substring(0,i+1));
list.add(s.substring(i+1,s.length()));
lists.add(list);
min = Math.min(toS(s.substring(0,i+1)) + toS(s.substring(i+1,s.length())),min);
}
} else {//分割后段的长度必须大于等于分割次数
for (int i = s.length() - m;i > 0;i--) {
min = Math.min(min,toS(s.substring(0,i)) + splitString(s.substring(i,s.length()),m-1));
}
}
Map<Integer,Integer> map = new HashMap<>(); map.put(m,min); memory.put(s,map);
return min;
}
public int toS(String s1) {
int c = 0;
for (int i = 0;i<s1.length()/2;i++) {
if (s1.charAt(i) != s1.charAt(s1.length() - i - 1)) {
c++;
}
}
return c;
}
要点:任采用递归,但增加记忆法用map存储s字段被分割成k次时需要的次数节省少量时间,在递归的时候时直接返回多个可能中的需修改次数最小值,节省时间,但还是慢