例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)。
因此,第二种方法总的空间复杂度为O(nlogn),时间复杂度为O(nlogn)。这样数据范围就能从1e2提升到1e5了
以上就是这道题动态规划优化的全部思维,下面附上代码:
#include
#include
#include
#include
using namespace std;
char s[500005];
int dp[500005] = {0};
vector 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;
}
转载请注明出处!!!
如果有写的不对或者不全面的地方 可通过主页的联系方式进行指正,谢谢