剑指 Offer II 086. 分割回文子字符串
题解
这题对于我而言,还是挺有难度的,学习了某位大神的做法
当我想到罗列出该字符串的所有回文子串后,我就卡住了,因为我想的回文子串是用一维的数组去表示的,没想到这里居然是可以用二维数组去表示的,如果可以想到二维数组应该就很容易想到对二维数组进行DFS
🚩这里还有一个“小心机”,我们用二维数组去记录i~j是否是回文时,可以不用1/0或者布尔类型去标识,直接用截取出来的回文子串,因为在我们需要频繁地用到回文子串,每次都通过切割的方式去获取效率有点低
📝代码如下:
List<String[]> combinations = new ArrayList<>();
String[][] palindromeArray;
/**
* @param s
* @return java.lang.String[][]
* @author 瞿莹莹
* @describe: 我模仿大神的做法
* @date 2022/3/28 8:35
*/
public String[][] partition(String s) {
// 字符串的长度
int len = s.length();
// 构建所有回文子串二维数组,数组的i行j列的值如果有,则说明字符串i到j是一个回文子串,且这个值就是这个回文子串
palindromeArray = new String[len][len];
buildPalindromeArray(s, len);
// 深度遍历回文子串二维数组,找出回文子串可以构成原来字符串的组合
dfsPalindromeArray(len, 0, new ArrayList<>());
// 返回结果
return combinations.toArray(new String[combinations.size()][len]);
}
/**
* @param s
* @param len
* @return void
* @author 瞿莹莹
* @describe: 构建所有回文子串二维数组
* @date 2022/3/28 8:43
*/
public void buildPalindromeArray(String s, int len) {
// 因为字符串的访问用的是charAt,且在循环中需要频繁访问,没有数组访问地快,所以将字符串用数组表示
char[] strArray = s.toCharArray();
// 从后面开始遍历字符串
for (int i = len - 1; i >= 0; i--) {
// 从这一位开始向后看,因为这一位的后一位在上一个循环中已经整理好了,我们可以借助它来判断这一位打头时是否可以找到回文
for (int j = i; j < len; j++) {
// i是子串head,j是子串tail,判断i~j是否是回文,
// 在head和tail字符相等的情况下,可以借助i+1~j-1是否是回文,
// 但是需要注意看i和j是否是相邻的 或者 i和j是否就是一个位置
if (strArray[i] == strArray[j] && (j - i <= 1 || palindromeArray[i + 1][j - 1] != null)) {
palindromeArray[i][j] = s.substring(i, j + 1);
}
}
}
}
/**
* @param len
* @param combination
* @return void
* @author 瞿莹莹
* @describe: 深度遍历回文子串二维数组
* @date 2022/3/28 9:05
*/
public void dfsPalindromeArray(int len, int rowIndex, List<String> combination) {
// 一次深度遍历的最后一层,就是发现上面行都遍历过了,这次要从最后一行的下一行开始遍历了
if (rowIndex >= len) {
// 说明dfs遍历完了,已经找到了一个回文组合
combinations.add(combination.toArray(new String[combination.size()]));
return;
}
// 遍历这一行的元素,对其的没有个非空元素进行dfs
// 这个矩阵数组是一个上三角数组,因为下三角部分表示的是i>j的部分,而在构建回文子串二维数组的时候,是从i<j的情况下考虑的
// 所以只需要遍历上三角
for (int j = rowIndex; j < len; j++) {
if (palindromeArray[rowIndex][j] != null) {
combination.add(palindromeArray[rowIndex][j]);
// 下一个深度遍历的rowIndex应该是这一个非空元素的所在列数+1
dfsPalindromeArray(len, j + 1, combination);
// 为这一行的下一个元素准备一个正确的组合(combination),删除这一行加入的新元素,也就是最后一个元素
// 为什么这里新加入的元素是最后一个元素,是因为上一步代码执行完成后,其他后加入list的元素都是已经访问完毕的状态,
// 访问完毕的数据就会从combination中移出去,因此到这一步时,这一行加入的元素就成了combination的最后一个元素
combination.remove(combination.size() - 1);
}
}
}
🏴☠️重点理解combination.remove(combination.size() - 1);
这段代码,为什么是删除最后一个元素。
总结心得
- 题目给的数据肯定需要遍历,遍历的过程中总结规律并且记录规律,记录规律的媒介有很多种,比如一维数组、二维数组等等,如果一维数组解决不了问题,就可以试着想到二维数组
- 对我们记录的规律又需要一次遍历,这次遍历是为了得到结果,拿二维数组来说,遍历的方式有很多种,比如DFS,BFS,循环枚举
- 当我们遍历一个东西的时候(可以是数据,也可以是我们从数据总结出来的规律),要从两个角度去思考,如果从head开始遍历不方便,那就从tail开始遍历,比如这题回文明显从后面开始遍历比较好,这题从后开始遍历然后就是动态规划的思想
Java知识UP
- String类型->Array类型:String类型变量的toCharArray方法,没有参数
- List类型->Array类型:List类型的toArray方法,参数比较特别,如果list里面的每一项是String[]类型的话,那就是一个二维数组,参数就可以是
new String[rowNum][colNum]
,感觉参数就是一个新的数组实例 - List删除某个元素可以用它的remove方法,参数就是那个元素的index