NOIP2018提高组金牌训练营——字符串专题
1154 回文串划分
有一个字符串S,求S最少可以被划分为多少个回文串。
例如:abbaabaa,有多种划分方式。
a|bb|aabaa - 3 个回文串
a|bb|a|aba|a - 5 个回文串
a|b|b|a|a|b|a|a - 8 个回文串
其中第1种划分方式的划分数量最少。
Input
输入字符串S(S的长度<= 5000)。
Output
输出最少的划分数量。
Input示例
abbaabaa
Output示例
3
复习了一波划分型dp
可以直接暴力处理出i到j是否是回文串,dp即可
但是这个暴力理论上是n^3的,但大多数情况下对于i到j两端不同很快就退出了,
所以还是可以过的,最大的数据达到0.5s
#include<bits/stdc++.h> #define REP(i, a, b) for(register int i = (a); i < (b); i++) #define _for(i, a, b) for(register int i = (a); i <= (b); i++) using namespace std; const int MAXN = 5000 + 10; int a[MAXN][MAXN], dp[MAXN]; char s[MAXN]; bool judge(int i, int j) { while(i < j) { if(s[i] != s[j]) return false; i++; j--; } return true; } int main() { scanf("%s", s + 1); int n = strlen(s + 1); _for(i, 1, n) _for(j, i, n) a[i][j] = judge(i, j); memset(dp, 0x3f, sizeof(dp)); dp[0] = 0; _for(j, 1, n) { _for(i, 1, j) if(a[i][j]) dp[j] = min(dp[j], dp[i-1] + 1); } printf("%d\n", dp[n]); return 0; }
但是讲解里面貌似有更快的方法。
一种是dp
如果s[i] == s[j], 那么S[i……j]是否是回文串取决于S[i+1……j-1]是否是回文串
可以用这个思路dp
但是实际上的时间却比暴力要慢(0.7S)
应该是循环内判断比较多,以及暴力大多数很快就判断出不是回文串的缘故
#include<bits/stdc++.h> #define REP(i, a, b) for(register int i = (a); i < (b); i++) #define _for(i, a, b) for(register int i = (a); i <= (b); i++) using namespace std; const int MAXN = 5000 + 10; int a[MAXN][MAXN], dp[MAXN]; char s[MAXN]; int main() { scanf("%s", s + 1); int n = strlen(s + 1); _for(d, 1, n) _for(l, 1, n) { int r = l + d - 1; if(r > n) break; if(s[l] == s[r]) { if(l + 1 >= r - 1) a[l][r] = 1; else a[l][r] = a[l+1][r-1]; } } memset(dp, 0x3f, sizeof(dp)); dp[0] = 0; _for(j, 1, n) { _for(i, 1, j) if(a[i][j]) dp[j] = min(dp[j], dp[i-1] + 1); } printf("%d\n", dp[n]); return 0; }
第二种是用Manacher
代码量会多很多,就不写了
所以还是暴力出奇迹