题目链接: http://codeforces.com/contest/835/problem/D
题意: 定义 k 度回文串为左半部分和右半部分为 k - 1 度的回文串 . 给出一个字符串 s, 问 1 ~ s.size() 度回文串的数目分别为多少 .
思路: 预处理 + 记忆化dp
可以先花 O(n^2) 的时间预处理一下所有字串是否为回文串 . 注意预处理时不能直接枚举 l, r, 不然会出现处理 [l, r] 时 [l + 1, r - 1] 并没有先处理的情况 .不过换个思路, 枚举长度和左端点就好了 .
然后可以直接 dfs 出所有字串的回文度, 因为对于回文串其左右部分是一样的, 所以每次 dfs 一半即可,
这里还可以加个记忆优化, 能将复杂度从 O(n * n * log(n)) 降到 O(n * n).
最后注意一下对于度为 k 的串, 其也是度为 1 ~ k - 1 的串 .
代码:

1 #include <iostream> 2 using namespace std; 3 4 const int MAXN = 5e3 + 10; 5 int vis[MAXN][MAXN];//vis[i][j]记录[i,j]是否为回文串 6 int dp[MAXN][MAXN];//dp[i][j]记录[i,j]的回文度 7 int sol[MAXN];//sol[i]记录回文度为i的子串数目 8 9 int dfs(int l, int r){ 10 if(l == r) return 1; 11 if(!vis[l][r]) return 0; 12 if(dp[l][r]) return dp[l][r]; 13 int len = r - l + 1; 14 dp[l][r] += dfs(l, l + len / 2 - 1) + 1; 15 return dp[l][r]; 16 } 17 18 int main(void){ 19 ios::sync_with_stdio(false), cin.tie(0), cout.tie(0); 20 string s; 21 cin >> s; 22 int len = s.size(); 23 for(int i = 0; i < len; i++){ 24 vis[i][i] = 1; 25 if(i + 1 < len && s[i] == s[i + 1]) vis[i][i + 1] = 1; 26 } 27 for(int i = 3; i <= len; i++){//i为长度 28 for(int j = 0; j + i - 1 < len; j++){//j为左边界 29 if(vis[j + 1][j + i - 2] && s[j] == s[j + i - 1]) vis[j][j + i - 1] = 1; 30 } 31 } 32 for(int i = 0; i < len; i++){ 33 for(int j = i; j < len; j++){ 34 sol[dfs(i, j)]++; 35 } 36 } 37 for(int i = len - 1; i > 0; i--){ 38 sol[i] += sol[i + 1]; 39 } 40 for(int i = 1; i <= len; i++){ 41 cout << sol[i] << " "; 42 } 43 cout << endl; 44 return 0; 45 }