题意:
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' );
}