时间限制:1000MS 内存限制:1000K 提交次数:169 通过次数:63 题型: 编程题 语言: G++;GCC Description 我们说一个字符串是回文串,那么意味着这个串从两边读起来的字母都是一样的。例如racecar是回文串, 然而fastcar则不是。 对一个串的划分意思是将一个串划分为若干个部分。例如,racecar可以划分为race 和car两部分。给出 一个串,要把这个串划分为若干个回文串,那么至少要把这个串划分为多少部分? 例如 'racecar'已经是回文串,划分为1 个部分即可(这个部分就是racecar)。 'fastcar' 需要被划分为七个部分 ('f', 'a', 's', 't', 'c', 'a', 'r')。根据回文串的定义,单个字母也是回文串。 'aaadbccb' 分成可以被分为三个回文串 ('aaa', 'd', 'bccb')。找不到更少的划分方法。 输入格式 输入的第一行是数字T,表示输入文件含有T个CASE。之后有T行,每行有一个长度不大于1000的字 符串,全部由小写字母组成,中间没有空格。 输出格式 对于每个CASE,输出一个数字,表示对该字符串的回文串最小划分。 输入样例 3 racecar fastcar aaadbccb 输出样例 1 7 3 提示 来源 PKKJ @ 07 GIS 1 dp[i]表示前i个字符最少能被划分为回文串的个数 转移就很简单了:dp[i] = min(dp[j - 1]) + 1; 其中 1 <= j <= i 并且 (s[j]~s[i])是回文串 直接算复杂度是O(n^3),显然不行,可以用mancher预处理出vis[i][j], 表示(s[i]~s[j])是否是回文串 #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #include <cmath> using namespace std; const int N = 1005; char s[N], str[N << 1]; bool vis[N][N]; int dp[N], p[N << 1], len; void get(int n) { int mx = 0, id = 0; for(int i = 0; i < n; ++i) { p[i] = mx > i ? min(p[2 * id - i], mx - i) : 1; while(str[i + p[i]] == str[i - p[i]]) p[i]++; if(i + p[i] > mx) { mx = i + p[i]; id = i; } } } void pre(int n) { memset(vis, false, sizeof vis); for(int i = 2; i < n; ++i) { if(i & 1) { int st = i - 1; int ed = i + 1; int to = i - p[i] + 1; while(st >= to) { vis[st >> 1][ed >> 1] = true; st -= 2; ed += 2; } }else { int id = i >> 1; vis[id][id] = true; int st = i - 2; int ed = i + 2; int to = i - p[i] + 1; while(st >= to) { vis[st >> 1][ed >> 1] = true; st -= 2; ed += 2; } } } } void init() { int n = 0; str[n++] = '$'; str[n++] = '#'; for(int i = 0; i < len; ++i) { str[n++] = s[i]; str[n++] = '#'; } str[n] = 0; get(n); pre(n); } void solve() { dp[0] = 0; for(int i = 1; i <= len; ++i) { dp[i] = 0x3f3f3f3f; for(int j = 1; j <= i; ++j) if(vis[j][i]) dp[i] = min(dp[i], dp[j - 1] + 1); } printf("%d\n", dp[len]); } int main() { int _; scanf("%d", &_); while(_ --) { scanf("%s", s); len = strlen(s); init(); solve(); } return 0; }