Codeforces 835D - Palindromic characteristics 【区间DP+回文串】


D. Palindromic characteristics


time limit per test 3seconds

memory limit per test     256megabytes


Palindromic characteristics of strings with length |s| is a sequence of |s| integers, where k-th number is the total number ofnon-empty substrings of s which are k-palindromes.

A string is1-palindrome if and only if it reads the same backward asforward.

A string isk-palindrome (k > 1) if and only if:

1.Its left half equals to its right half.

2.Its left and right halfs are non-empty (k - 1)-palindromes.

The left half of stringt is its prefix of length |t| / 2, and right half — thesuffix of the same length. |t| / 2denotes the length of string t divided by 2, rounded down.

Note that each substring is counted as many times as it appearsin the string. For example, in the string "aaa" the substring"a" appears 3 times.

Input

The first line contains the strings (1 ≤ |s| ≤ 5000) consisting oflowercase English letters.

Output

Print|s| integers — palindromic characteristics of string s.

Examples

Input

abba

Output

6 1 0 0

Input

abacaba

Output

12 4 1 0 0 0 0

Note

In the first example 1-palindromes are substring «a», «b», «b», «a», «bb», «abba»,the substring «bb» is 2-palindrome. There are no 3- and 4-palindromes here.

 



【题意】

给出一个字符串,问它分别具有多少个k级字符串。


一个回文串叫做1级回文串,一个回文串为k级回文串当且仅当它的左半部分和右半部分相同,且两部分都是(k-1)级回文串。


【思路】


显然我们发现k级字符串的得到需要(k-1)级字符串的铺垫,那么就知道可以用区间DP来解决这个问题。


我们用dp[i][j]来表示s[i]~s[j] 这一段字符串是几级回文串,如果它是一个回文串,那么在s[i-1]==s[j+1]的情况下,s[i-1]~s[j+1]就是回文串了,且其级数为前一个回文串左边一半的级数+1.


dp[i-1][j+1]=dp[i][(i+j)/2]+1.


有了这个状态转移方程基本就大功告成了。


#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
#define mst(a,b) memset((a),(b),sizeof(a))
#define rush() int T;scanf("%d",&T);while(T--)

typedef long long  ll;
const int maxn = 5005;
const ll mod = 1e9+7;
const int INF = 0x3f3f3f;
const double eps = 1e-9;

ll dp[maxn][maxn];               //s[i]~s[j]字符串的级数
bool flag[maxn][maxn];           //s[i]~s[j]是否为回文串
ll ans[maxn]; 
char s[maxn];

int main()
{
    while(~scanf("%s",s+1))
    {
        mst(dp,0);
        mst(flag,0);
        mst(ans,0);
        int n=strlen(s+1);
        for(int i=1;i<=n;i++)     //预处理
        {
            dp[i][i]=1;
            flag[i][i]=1;
        }
        for(int len=1;len<=n;len++)
        for(int i=1;i<=n;i++)
        {
            int j=i+len;
            if(j>n) continue;
            if(s[i]==s[j])
            {
                if(j-i==1||flag[i+1][j-1])
                {
                    int m=(i+j)/2;
                    if((j-i+1)&1) m--;
                    flag[i][j]=1;
                    dp[i][j]=dp[i][m]+1;
                }
            }
        }
        for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        {
            ans[dp[i][j]]++;
        }
        for(int i=n-1;i>=1;i--)
        {
            ans[i]+=ans[i+1];           //级别高的回文串一定也是级别低的回文串,比如是五级回文串的话,就一定是四(3,2,1)级回文串
        }
        for(int i=1;i<n;i++)
        {
            printf("%I64d ",ans[i]);
        }
        printf("%I64d\n",ans[n]);
    }
    return 0;
}















区间DP是一种动态规划的方法,用于解决区间范围内的问题。在Codeforces竞赛中,区间DP经常被用于解决一些复杂的字符串或序列相关的问题。 在区间DP中,dp[i][j]表示第一个序列前i个元素和第二个序列前j个元素的最优解。具体的转移方程会根据具体的问题而变化,但是通常会涉及到比较两个序列的元素是否相等,然后根据不同的情况进行状态转移。 对于区间长度为1的情况,可以先进行初始化,然后再通过枚举区间长度和区间左端点,计算出dp[i][j]的值。 以下是一个示例代码,展示了如何使用区间DP来解决一个字符串匹配的问题: #include <cstdio> #include <cstring> #include <string> #include <iostream> #include <algorithm> using namespace std; const int maxn=510; const int inf=0x3f3f3f3f; int n,dp[maxn][maxn]; char s[maxn]; int main() { scanf("%d", &n); scanf("%s", s + 1); for(int i = 1; i <= n; i++) dp[i][i] = 1; for(int i = 1; i <= n; i++) { if(s[i] == s[i - 1]) dp[i][i - 1] = 1; else dp[i][i - 1] = 2; } for(int len = 3; len <= n; len++) { int r; for(int l = 1; l + len - 1 <= n; l++) { r = l + len - 1; dp[l][r] = inf; if(s[l] == s[r]) dp[l][r] = min(dp[l + 1][r], dp[l][r - 1]); else { for(int k = l; k <= r; k++) { dp[l][r] = min(dp[l][r], dp[l][k] + dp[k + 1][r]); } } } } printf("%d\n", dp[n]); return 0; } 希望这个例子能帮助你理解区间DP的基本思想和应用方法。如果你还有其他问题,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值