2015 Multi-University Training Contest 6 J(hdu5362 Just A String)

本文探讨了如何计算由m种字母构成的所有长度为n的字符串中,可以通过重新排列形成回文串的数量。通过定义dp[i][j]来表示长度为i的字符串中有j个字符出现奇数次的情况数量,并利用动态规划的方法进行求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Problem Description
soda has a random string of length n which is generated by the following algorithm: each of n characters of the string is equiprobably chosen from the alphabet of size m.

For a string s, if we can reorder the letters in string s so as to get a palindrome, then we call s a good string.

soda wants to know the expected number of good substrings in the random string.
 

Input
There are multiple test cases. The first line of input contains an integer T, indicating the number of test cases. For each test case:

The first line contains two integers n and m (1≤n,m≤2000).
 

Output
For each case, if the expected number is E, a single integer denotes E⋅mn mod 1000000007.
 

Sample Input
3
2 2
3 2
10 3
 

Sample Output
10
40
1908021
题意:给出m种字母,问从这m种字母中选出若干种能组成长度为n的字符串有多少种方案,且要求字符串重排后是回文串。

思路:组合+dp

来自:https://blog.youkuaiyun.com/firenet1/article/details/47339943

解析:
      乘M^n意味着对所有可能的长度为n的字符串,求出它的是好的子串的个数的累加和。
如果长度为奇数的子串,那么该子串的字母中除了一种字母是奇数外,其他字母必须是偶数个。
如果长度为偶数,那么每种字母都必须是偶数个。-------------------显然是吧~~~
接下来定义状态:
       dp[i][j] 表示长度为i的有j种字母是奇数个的串的个数。
      那么dp[i][j] 只能是从dp[i-1][j-1]-------------表示添加一个原来字母数时偶数的字母字母后,奇数个字母的种数增加了,
                                                                             那么添加的字母只能是已经包含的奇数个数字母以外的字母了有m-j+1
                                       dp[i-1][j+1] ----------- 表示添加一个原来是奇数个的字母,那么奇数个字母的种数减少1,
                                                                         那么只能添加原来是奇数的字母了,有j+1种字母
dp[i][j] = dp[i-1][j-1]]*(m-j+1) + dp[i-1][j+1]*(j+1)  

代码:

来自:https://blog.youkuaiyun.com/firenet1/article/details/47339943

#define maxn 2048
int dp[maxn][maxn],mod=1000000007;
int po[maxn];
int main()
{
    int t,m,n;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        dp[1][0] = 0; //初始话dp[1],以及后面直接计算dp[n]都是为了减少计算(毕竟1000组case)
        dp[1][1] = m;
        po[0] = 1;
        for(int i = 1;i <= n;i++)
        {
            po[i] = (ll)po[i-1]*m%mod;
        }
        int j;
        for(int i = 2;i < n; i++)
        {
            dp[i][0] = dp[i-1][1];
            dp[i][i] = (ll)dp[i-1][i-1]*(m-i+1) % mod ;
            if(i & 1) j = 1;
            else j=2;

            for(;j<i;j+=2)
                dp[i][j]=((ll)dp[i-1][j-1]*(m-j+1)+(ll)dp[i-1][j+1]*(j+1))%mod;
        }
        if(n>1) dp[n][0]=dp[n-1][1];
        if(n>1) dp[n][1]=((ll)dp[n-1][0]*m+(ll)dp[n-1][2]*2)%mod;
        int ans = 0;
        for(int i = 1;i <= n; i++)
        {
            ans = (ans+(ll)dp[i][i&1]*(n-i+1)%mod*po[n-i])%mod;
        }
        printf("%d\n",ans);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值