319场周赛 不重叠回文子字符串的最大数目

给你一个字符串 s 和一个 正 整数 k 。

从字符串 s 中选出一组满足下述条件且 不重叠 的子字符串:

每个子字符串的长度 至少 为 k 。
每个子字符串是一个 回文串 。
返回最优方案中能选择的子字符串的 最大 数目。

子字符串 是字符串中一个连续的字符序列。

示例 1 :

输入:s = "abaccdbbd", k = 3
输出:2
解释:可以选择 s = "abaccdbbd" 中斜体加粗的子字符串。"aba" 和 "dbbd" 都是回文,且长度至少为 k = 3 。
可以证明,无法选出两个以上的有效子字符串。
示例 2 :

输入:s = "adbcda", k = 2
输出:0
解释:字符串中不存在长度至少为 2 的回文子字符串。

提示:

1 <= k <= s.length <= 2000
s 仅由小写英文字母组成

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/maximum-number-of-non-overlapping-palindrome-substrings
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

本文也是两问。第一个就是如何求解不重叠子字符串的最大数目。采用的是动态规划算法。动态规划算法主要是从两个方面进行考虑,一个是状态表示,一个是状态划分。状态表示可以使用一维的数组进行表示,f[i]表示前i字符的不重叠回文子字符串的最大数目。状态划分就是要将f[i]划分为多个之前不同状态的不相交集合,以便通过已经求解的f[i]之前的结果求解f[i]。如果最后一个回文子字符串不包含字符i,则按照最后一个回文子字符串的长度进行划分,长度至少为k,左端点的至少为i - k + 1,记为j,如果s[j][i]为回文字符串,那么f[i] = max(f[i],f[j-1])。
第二个就是判断s[j][i]是不是回文字符串,需要预处理出g[i][j],用来判断每一个子字符串是不是回文字符串。纯粹的做法就是遍历所有的子字符串并判断每个子字符串是不是回文字符串,时间复杂度为\(O(n ^ 3)\)。较为优雅的写法是通过递归来做,判断s[i][j]是不是回文字符串,需要判断s[i+1]和s[j-1]是否相同,并且s[i+1][j-1]是不是回文字符串。可以从长度1开始遍历,遍历每一个起点的所有长度。

code

class Solution {
public:

    //dp
    //状态表示:f[i]到字符i为止的不重叠回文子字符串的最大数目
    //状态划分:最后一段回文子字符串不包含字符i,f[i] = f[i-1]
    //最后一段回文子字符串包含i,根据最后一段回文字符串的长度进行划分,长度至少为k,也就是左端点i - k +1往左 
    int maxPalindromes(string s, int k) {
        int n = s.size();

        vector<vector<int>> g(n + 1,vector<int>(n + 1,0));

        for(int len = 1;len <= n;len ++)
        {
            for(int i = 1;i + len - 1 <= n;i ++)
            {
                int  j = i + len - 1;
                if(s[i-1] == s[j-1] && (len <= 2 || g[i + 1][j -1]))
                    g[i][j] = 1;
            }
        }

        vector<int> f(n +1,0);

        for(int i = 1;i <= n;i ++)
        {
            f[i] = f[i-1];

            for(int j = i - k + 1;j >= 0;j--)
            {
                if(g[j][i]) f[i] = max(f[i],f[j-1] + 1);
            }
        }

        return f[n];
    }
};
posted on 2022-11-15 21:14  huangxk23  阅读(37)  评论(0)    收藏  举报