Codeforces Round #427 (Div. 2) D.Palindromic characteristics【DP、后缀和】

D. Palindromic characteristics

题意:

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]; //表示从i到j的回文等级为dp[i][j]
int ans[5050];
int main() {
    string s;
    cin >> s;
    int n = s.size();
    memset(ans, 0, sizeof(ans));
    memset(dp, 0, sizeof(dp));
    //len=1时,回文等级等于1
    for(int i = 0; i < n; ++ i)
        dp[i][i] = 1;
    //len=2时,如果两个相等,那回文等级等于2,不然就不是回文串
    for(int i = 0; i < n - 1; ++ i)
        dp[i][i+1] = (s[i] == s[i+1] ? 2 : 0);
    //从len=3开始,n2遍历求dp[i][j],长度大的可以由长度小的递推而来,所以len从小到大遍历
    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;
            }

            //自己拿个纸画下就知道边界怎么写
            //由于已经知道字符串是回文串,所以它的结果就等于左边/右边等级+1
            //如果左边不是回文串,那么0+1就是1,也就是整个字符串回文等级为1
            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;
            }
        }
    }
    //统计下最大等级为i的个数ans[i]
    for(int len = 1; len <= n; ++ len) {
        for(int l = 0; l <= n - len; ++ l) {
            ans[dp[l][l + len - 1]]++;
        }
    }
    //求后缀和。因为k回文一定是k-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');
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章