题意:
k-回文的定义:
1.它的左半部分等于右半部分,即本身是1-回文。
2.它的左半部分和右半部分都是(k-1)-回文,奇数长度不考虑正中间。
给你一串长度为n的字符串,让你统计其所有子串中,是1-回文到n-回文的个数,并输出。
思路:
1.暴力枚举所有子串,计算它们的最高回文等级。
2.dp[i][j]
表示从第i位到第j位的最高回文等级为dp[i][j]
。
3.预处理长度小于等于2的子串。
4.先判断计算的子串是不是回文串,如果不是回文串则dp[i][j] = 0
。如果是回文串,即满足题意中的1。
5.计算的子串长度为偶数时,dp[l][r] = dp[l][mid] + 1
。
6.计算的子串长度为奇数时,dp[l][r] = dp[l][mid-1] + 1
。
7.统计一下后缀和就是结果,因为k回文一定是k-1回文。
代码:
#include <bits/stdc++.h>
using namespace std;
int dp[5050][5050];
int ans[5050];
int main() {
string s;
cin >> s;
int n = s.size();
memset(ans, 0, sizeof(ans));
memset(dp, 0, sizeof(dp));
for(int i = 0; i < n; ++ i)
dp[i][i] = 1;
for(int i = 0; i < n - 1; ++ i)
dp[i][i+1] = (s[i] == s[i+1] ? 2 : 0);
for(int len = 3; len <= n; ++ len) {
for(int l = 0; l <= n - len; ++ l) {
int r = l + len - 1;
if(s[l] != s[r] || dp[l + 1][r - 1] == 0) {
dp[l][r] = 0;
continue;
}
int mid = l + r >> 1;
if(len % 2 == 0) {
dp[l][r] = dp[l][mid] + 1;
} else {
dp[l][r] = dp[l][mid - 1] + 1;
}
}
}
for(int len = 1; len <= n; ++ len) {
for(int l = 0; l <= n - len; ++ l) {
ans[dp[l][l + len - 1]]++;
}
}
for(int i = n - 1; i >= 1; -- i) {
ans[i] += ans[i + 1];
}
for(int i = 1; i <= n; ++ i) {
if(i > 1) putchar(' ');
printf("%d", ans[i]);
}
putchar('\n');
}