CodeForces 360 C.Levko and Strings(组合数学+dp)

本文介绍了一种算法,用于计算特定字符串与其所有可能变化形式的相关度,并统计具有特定相关度值的字符串数量。通过动态规划的方法,文章详细阐述了如何有效地解决这一问题。

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

Description

给出一个长度为n的只由小写字母组成的字符串,定义另一个长度为n的只由小写字母组成的字符串ts的相关度为二元组(i,j)的对数,其中i,j需满足1ijn,t[i,...,j]字典序大于s[i,...,j],问和s相关度为k的字符串t的个数

Input

第一行两个整数n,k表示字符串长度和相关度,之后输入一个长度为n,只由小写字母组成的字符串s(1n2000,0k2000)

Output

输出和s相关度为k的字符串个数,结果模109+7

Sample Input

2 2
yz

Sample Output

26

Solution

dp[i][j]表示t的前i个字符已经放好,后面的字符和s相同且产生了j个相关度的方案数

假设st在第i个位置之前第一个不同的位置在l,即s[l+1,...,i1]=t[l+1,...,i1],那么当第i个位置t[i]取的比s[i]大时,起点在[l+1,i]之间,终点在[i,n]之间的区间都会产生一个相关度,而对于起点在[1,l]之间,终点在[l+1,n]之间的区间,st不同的位置必然在l+1之前,第i个位置怎么填不会影响其对相关度的贡献,其相关度的贡献之前已经算过了,故有转移方程dp[i][j]+=(zs[i])l=0i1dp[il][j(il)(ni+1)];如果第i个位置t[i]取的比s[i]小时,不会产生新的相关度,故有dp[i][j]+=(s[i]a)l=0i1dp[l][j],用sum[j]维护l=0i1dp[l][j],则这块O(1)转移,最后i=0ndp[i][k]即为答案

Code

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
const int INF=0x3f3f3f3f,maxn=2005;
#define mod 1000000007
int n,k,dp[maxn][maxn],sum[maxn];
char s[maxn];
void add(int &x,int y)
{
    x=x+y>=mod?x+y-mod:x+y;
}
int Solve()
{
    sum[0]=dp[0][0]=1;
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<=k;j++)
        {
            for(int l=i-1;l>=0&&(i-l)*(n-i+1)<=j;l--)
                add(dp[i][j],dp[l][j-(i-l)*(n-i+1)]);
            dp[i][j]=(ll)('z'-s[i])*dp[i][j]%mod;
            add(dp[i][j],(ll)(s[i]-'a')*sum[j]%mod);
            add(sum[j],dp[i][j]);
        }
    }
    int ans=0;
    for(int i=0;i<=n;i++)add(ans,dp[i][k]); 
    return ans;
}
int main()
{
    scanf("%d%d",&n,&k);
    scanf("%s",s+1);
    printf("%d\n",Solve());
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值