携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第15天,点击查看活动详情
132. 分割回文串 II
给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是回文。
返回符合要求的 最少分割次数 。
示例 1:
输入:s = "aab"
输出:1
解释:只需一次分割就可将 s 分割成 ["aa","b"] 这样两个回文子串。
示例 2:
输入:s = "a"
输出:0
示例 3:
输入:s = "ab"
输出:1
数据范围:
1 <= s.length <= 2000
s 仅由小写英文字母组成
思路
字符串分割的最少回文
怎么求?
首先考虑这样一个问题,假设当前长度为x时,最后一个回文串在末尾且长度为y
则 当前的分割为 x - y 处的分割 加上1
写出式子就是
那么,我们就可以顺利写出一个dp了
浅分析一下时间复杂度呢?
那么显然,时间复杂度是 length为每次的时间判断回文串
数据范围是 2000,显然过不了,我们需要优化一下
如何优化呢, dp显然已经干不动了
我们可以优化哪一部分呢 ?
如何判断一个字符串是不是回文?
预处理了这个字符串的hash好像是可以办到的
那么,我们可以考虑这一件事情
字符哈希预处理求是否为回文
预处理时间是的,算回文是的
这样我们就可以把时间优化到 的时间复杂度了
代码
class Solution {
public:
int minCut(string s) {
using ULL = unsigned long long;
const int P = 131;
int n = s.size();
vector<ULL> hash(n + 2), rhash(n + 2), p(n + 2, 1);
vector<int> dp(n + 1);
for (int i = 1; i <= n; i ++) {
hash[i] = hash[i-1] * P + s[i-1];
p[i] = p[i-1] * P;
}
for (int i = n; i >= 1; i --) {
rhash[i] = rhash[i+1] * P + s[i-1];
}
auto get = [&](int l, int r) {
return hash[r] - hash[l-1] * p[r-l+1];
};
auto rget = [&](int l, int r) {
return rhash[l] - rhash[r+1] * p[r-l+1];
};
auto check = [&] (int l, int r) {
int k = l + r >> 1;
if ((r - l + 1) % 2) {
return get(l, k) == rget(k, r);
} else {
return get(l, k) == rget(k+1, r);
}
};
for (int i = 2; i <= n; i ++) {
if (check(1, i)) {
dp[i] = 0;
continue;
}
dp[i] = dp[i-1] + 1;
for (int j = 1; j < i; j ++)
if (check(j+1, i)) {
dp[i] = min(dp[i], dp[j] + 1);
}
}
return dp[n];
}
};