动态规划优化问题-3

例2:切割回文串

​ 给出一个字符串s,对字符串最少切几刀,可以使得切完后的每一段字符串都是回文串(单一字符也是回文串)?

​ 这道题我们首先能想到用区间dp来做,设置dp[i][j]代表字符串区间[i, j]最少切多少刀使得每一段字符串都是回文串,这时我们就可以得到递推方程:

//当[i, j]是回文数的时候,dp[i][j] = 0;
//当[i, j]不是回文数的时候,dp[i][j] = min(dp[i][k] + dp[k + 1][j] + 1) //其中i <= k < j
//而对于[i, j]是否为回文数,需要利用bool数组maps[i][j]来记录和预处理

​ 上面方法的空间复杂度是O(n ^ 2),时间复杂度是O(n ^ 3),预处理时间复杂度也为O(n^3),因此当n为1e5左右时直接爆炸。

​ 和上一篇文章一样。大家先在这里停住,考虑一下如何将上面的思路,缩减为空间复杂度为O(n),时间复杂度为O(nlogn)再继续往下看

​ 对于空间复杂度。我们需要一个数组maps[i][j]用来记录字符串区间[i, j]是否为回文串,对于这个二维数组,我们可以怎么优化呢?对,把它变为一个动态数组maps[i],用于存储以i结尾的回文串的起始位置。(由于出题人生成的都是随机数据,所以1e5个字符全是相同的那种极限样例根本不可能出现,出现的概率不亚于天天中大乐透~Orz)

​ 这时,maps数组从O(n ^ 2)优化成了O(k)(k为回文串的个数),我们就需要来考虑预处理回文串的时间复杂度优化问题了。由于极限数据根本不可能出现,因此我们可以通过便利中点i,让起点star和终点endd从i向两边扩,若s[star] != s[endd]时即跳出循环,并把全部符合条件的star加入到maps[endd]中,这样预处理的时间复杂度就为O(nlogn)

​ 考虑完预处理,接下来我们开始考虑dp数组的空间优化和状态转移的时间优化~

​ dp数组上面方法中是二维的,因此我们需要优化成一维数组。用dp[i]来代表前i个字符串最少切的刀数。

​ 优化成一维数组后,我们紧跟着推出递推方程:

dp[i] = dp[j] + 1;	//[j + 1, i]必须是回文字符串

​ 由于数据是随机的,因此满足条件的j不会特别多。由于maps[i]中存了全部满足条件的j,因此dp的时间复杂度大概也为O(nlogn)。

扫描二维码关注公众号,回复: 5013455 查看本文章

​ 因此,第二种方法总的空间复杂度为O(nlogn),时间复杂度为O(nlogn)。这样数据范围就能从1e2提升到1e5了

​ 以上就是这道题动态规划优化的全部思维,下面附上代码:

#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <vector>
using namespace std;

char s[500005];
int dp[500005] = {0};
vector <int> zcy[500005];

int main () {
    scanf("%s", s + 1);
    int n = strlen(s + 1);
    for (int i = 1; i <= n; i++) {
        zcy[i].push_back(i);
        if (i + 1 <= n && s[i + 1] == s[i])
            zcy[i + 1].push_back(i);
    }
    for (int i = 1; i <= n; i++) {
        int p, q;
        for (p = i + 1, q = i - 1; p <= n && q >= 1; p++, q--) {
            if (s[p] == s[q]) {
                zcy[p].push_back(q);
            } else {
                break;
            }
        }
        if (s[i] == s[i + 1]) {
            for (p = i + 2, q = i - 1; p <= n && q >= 1; p++, q--) {
                if (s[p] == s[q]) {
                    zcy[p].push_back(q);
                } else {
                    break;
                }
            }
        }
    }
    memset(dp, 0x3f, sizeof(dp));
    dp[1] = 0;
    for (int i = 2; i <= n; i++) {
        int flag = 0;
        for (int j = 0; j < zcy[i].size(); j++) {
            if (zcy[i][j] == 1) {
                flag = 1;
                break;
            }
        }
        if (flag) {
            dp[i] = 0;
            continue;
        }
        for (int j = 0; j < zcy[i].size(); j++) {
            dp[i] = min (dp[i], dp[zcy[i][j] - 1] + 1);
        }
    }
    printf("%d\n", dp[n]);
    return 0;
}

转载请注明出处!!!

如果有写的不对或者不全面的地方 可通过主页的联系方式进行指正,谢谢

猜你喜欢

转载自blog.csdn.net/Ivan_zcy/article/details/86505296