Partitioning by Palindromes UVA - 11584
VOJ题目位置:UVA - 11584
这道题主要是找到最小回文串的个数,我们可以把从1到达n个字符串看成从1到2,再从1到3等等到子阶段来解决,毫无疑问这样采用的是dp方法。
我们采用dp[i]表示前i个字符(下标从0到i-1)构成的字串的最小回文串的个数,由于dp[i]的第i个(下标为i-1)可能和前面的构成一个回文,所以得遍历从1到i-1的情况(设当前遍历的变量为j),如果下标j到i-1的子字符串为回文,则dp[i] = min(dp[i], dp[j] + 1);因为从j到i-1是一个回文字符串。
但是如何快速判断j到i-1是否为回文呢?可以采用预处理到方法,即若回文字符串为奇数个,则遍历字符串的中间i,设置变量j,一开始为0,然后逐渐加1,字符串左边就为i-j,右边就为i+j,每次j加一就相当于i往左右扩大了1个,判断此时是否为回文字符串,一直加到不为回文字符串为止;若为偶数个,则设定好中间两个左边为i,右边为i+j,j一开始为0,然后j逐渐加1,左边就为i-j+1,右边就为i+j。复杂度O(n2)
for(int i = 0; i < n; i++) { for(int j = 0; i - j >= 0 && i + j < n; j++)//奇数个情况,i为中间的下标,j为往左右两边扩张的个数,易得左边界为i - j, 右边界为i + j { if(temp[i - j] != temp[i + j])//如果此时扩张字符串后不为回文字符串了,因为一开始字符串只有一个字符必定为回文,
//然后每次扩张一个只需要判断左右边界是否相同即可,如果不相同则说明此时开始,以i为中心的子字符串现在以及之后都不会是回文的了。 break; else is_p[i - j][i + j] = true; } for(int j = 1; i - j + 1 >= 0 && i + j < n; j++)//偶数个情况,i为中间两个的左边下标,一开始j=1,i-j+1为左边界,i+j为右边界 { if(temp[i - j + 1] != temp[i + j]) break; else is_p[i - j + 1][i + j] = true; } }
完整代码:
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; char temp[1005]; bool is_p[1005][1005]; int dp[1005]; int main() { int t, n; scanf("%d", &t); for(int cnt = 0; cnt < t; cnt++) { memset(is_p, 0, sizeof(is_p)); scanf("%s", temp); n = strlen(temp); for(int i = 0; i < n; i++) { for(int j = 0; i - j >= 0 && i + j < n; j++) { if(temp[i - j] != temp[i + j]) break; else is_p[i - j][i + j] = true; } for(int j = 1; i - j + 1 >= 0 && i + j < n; j++) { if(temp[i - j + 1] != temp[i + j]) break; else is_p[i - j + 1][i + j] = true; } } for(int i = 0; i <= n; i++) dp[i] = i;//默认前i个的最大回文字符串个数就为字符串的个数 for (int i = 1; i <= n; ++i) { for (int j = 0; j < i; ++j) { if(is_p[j][i - 1]) dp[i] = min(dp[i], dp[j] + 1); } } printf("%d\n", dp[n]); } return 0; }