(7)回文串分割

回文串分割

问题描述:
给定一个字符串s,把 s 分割成回文串,求最小的分割次数
s =”aab”,返回1,分割为”aa”,”b”
回文串:正着读和反着读结果都一样的字符串称为回文串”aabbaa”

(1)描述
F(i) 表示长度为 i 的字符串分割成回文串的最小分割次数
(2)递推演算

(a)因为对于一个长度为 i 的字符串来说,最大的切割数为 i – 1,所以我们可以将F(i) 进行初始化为 i – 1
(b)对于长度为 i 的字符串,其实在其F(i -1) 的基础上再切一刀就可以保证切成回文字符串了,例如,aab 为1,aabc为 2
(c)看上去这样就可以了,但是万一第 i 个字符串和前面的字符串又重新匹配成回文串了,例如,aabc 为2,aabcb 为 1
(d)还有这种重复的情况 a 为 0, aa 为 0,aaa 为 0
所以当我们新加一个字符的时候,我们不能只是单纯的加上1就可以
(e)我们需要重新从字符串的头部开始查找,是否还会组成新的回文串
例如字符串长度为 i,我们从 j = 0, 开始遍历
若j+1 ~i 之间的字符串刚好是一个回文串,即新加进来的第 i 个字符恰巧与旧的字符串中部分字符串组成了回文串
此时就需要将 F(j) + 1 和 F(i) 进行比较,将 F(i) 更新为两者之间最小的一个
(f)并且这里有一个点,F(0) 的值如何确定呢?
就拿 “a” 来说,F(1) = 0 ; F(1) = F(0) + 1 则F(0) = -1
“aa” F(1) = 0; F(2) = 0 ;
在确定 F(2) 的时候,仍旧是从头开始遍历 从 j = 0 开始遍历,F(2) 初始为 1,F(1) 为 0
第一次 F(0) + 1 和 F(2) 之间的最小值为0,将F(2) 更新为 0,
第二次 F(1) + 1 和 F(2) 之间的最小值为0,F(2) 不变

(3)初始化
F(i) = i – 1
(4)返回结果
F(s.size())

代码展示

class Solution {
public:

    //这个函数是实现判断s[i] 到 s[j] 之间的字符组成的字符串是不是回文串
    void judge_roll(string s, vector< vector<bool>> &v1)
    {
        int len = s.size();
        if(len < 0) return;
        int i =0 ,j = 0;
        for(i = len - 1;i >=0 ;--i)
        {
            for(j = i; j < len ;++j )
            {
               if(i == j) v1[i][j] = true;
                else
               if(j == i+1 && s[i] == s[j] ) v1[i][j] = true;
                else
                 v1[i][j] = (s[i] == s[j]) && v1[i+1][j-1]; 
            }
        }
    }

    int minCut(string s) {
        int len = s.size();
        if(len <= 0 ) return 0;

        int i = 0, j = 0;
        vector< vector<bool> > isRoll(len , vector<bool>(len ,false));
        judge_roll(s,isRoll);
        //初始化
        vector<int> ret(len+1, 0);
        for(i =0 ; i <= len ;++i)
        {
            ret[i] = i-1;
        }

        for(i = 1 ; i <= len ;++i)
        {
        //这里注意下标,因为ret的有效下标是从 1 开始的
            for(j = 0;j < i; ++j)
            {
                if(isRoll[j][i-1])
                    ret[i] = min(ret[i],ret[j] + 1);
            }

        }
        return ret[len];
    }
};
点赞