[每日一题] leetcode 132. 分割回文串 II

107 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 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[x]=dp[xy]+1dp[x] = dp[x-y] + 1
那么,我们就可以顺利写出一个dp了

浅分析一下时间复杂度呢?
那么显然,时间复杂度是 O(n2×O(length))O(n^2 \times O(length)) length为每次O(n)O(n)的时间判断回文串

数据范围是 2000,显然过不了,我们需要优化一下
如何优化呢, dp显然已经干不动了
我们可以优化哪一部分呢 ?
如何O(1)O(1)判断一个字符串是不是回文?
预处理了这个字符串的hash好像是可以办到的
那么,我们可以考虑这一件事情
字符哈希预处理求是否为回文
预处理时间是O(n)O(n)的,算回文是O(1)O(1)
这样我们就可以把时间优化到 O(n2)+O(n)O(n^2) + O(n)的时间复杂度了

代码

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];
    }
};