题目
给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。
返回 s 所有可能的分割方案。
示例:
输入: "aab"
输出:
[
["aa","b"],
["a","a","b"]
]
题解
暂无官方题解
看到之后,感觉暴力法还是很容易想到的:递归找回文串,然后找到最后就完事了,把一个符合要求的含有String的List压入最后返回的List,然后依次类推回溯找到所有的String List。这样算下来复杂度有O(n3),这个方法肯定可以优化。
自己就去想能不能马拉车优化一下回文串的判断,显然是可以的。马拉车得到辅助数组p[](复杂度O(n)),然后利用p[]构造一个’#'之间的回文单向转移图(只会从左向右转移)(复杂度O(n2)),再dfs遍历那个回文单向转移图(复杂度O(n2)),就可以得到所需的全部字符串List了。
但是这个实现起来,我到dfs那里就不会了。。。Java没有全局变量,然后我自己也不太会Java的数据结构实现,于是就搁置了,看看评论区的大佬。
然后就发现一位用动归+回溯法解决的大佬:(6ms,超过92%,复杂度算下来应该也是O(n2) ? )
//执行用时 : 6 ms, 在Palindrome Partitioning的Java提交中击败了92.79% 的用户
//内存消耗 : 50.5 MB, 在Palindrome Partitioning的Java提交中击败了39.73% 的用户
class Solution {
int len;
ArrayList<List<String>> res = new ArrayList<>();
String s;
boolean[][] dp;
public List<List<String>> partition(String s) {
this.s = s;
len = s.length();
if (len < 1)
return res;
// dp[i][j] 表示某一子串,s.substring(i, j + 1)
// 例如 s="babad",dp[0][0] = "b",dp[0][4] = "babad"
dp = new boolean[len][len];
// one character
// 斜着遍历 [0,0] -> [1,1] -> ...
// 单个字符均为回文
for (int i = 0; i < len; i++) {
dp[i][i] = true;
}
// two character
// 斜着遍历 [0,1] -> [1,2] -> ...
// 两个字符均相同才是回文
for (int i = 0; i < len - 1; i++) {
dp[i][i + 1] = s.charAt(i) == s.charAt(i + 1);
}
// others
// 开始dp, 此子串 = 字符 + 左下角的子串 + 字符
// 只有左下角是回文,同时两端添加的字符相同时,才是回文
for (int i = 2; i < len; i++) {
for (int j = 0; j < len - i; j++) {
dp[j][j + i] = dp[j + 1][j + i - 1] && s.charAt(j) == s.charAt(j + i);
}
}
//回溯法,穿串串
foo(new LinkedList<>(),0);
return res;
}
void foo(LinkedList<String> path, int level) {
if (level >= len) {
res.add(new ArrayList<>(path));
return;
}
for (int i = level; i < len; i++) {
if (dp[level][i]) {
path.add(s.substring(level, i + 1));
foo(path, i + 1);
path.removeLast();
}
}
}
}
感想
不知道我的思路实现之后大概在什么位置,马拉车应该也算是dp简化了开始的回文判定的部分吧。。。希望能和大佬的6ms成绩不差太多。日后有机会回来实现一下试试。反正其他的评论区答案——那些递归回溯或者dfs回溯的基本都要16ms。还是dp牛逼啊。。。