CF156C Cipher

洛谷题目 CF原题

见过计数DP,也见过字符串的计数DP,但是这样的是第一次见。

Solution

看到要求方案数,那么一定是计数DP了。这字符串你还想用组合数?

但是设计什么状态能够得到最后答案呢?限制有点多诶^_~

发现题面中的两种运算都是对称的,一个往字母表后,另一个必定往前。

等等,字母表?ASCII码?

哦~一个ASCII码增加 \(x\) ,另一个减少 \(x\) 吖(o゚v゚)ノ(注意不是一个位置只能运算一次)。也就是说整个字符串的ASCII码之和是不变的。

那么可以设 \(dp_{i,j}\) 表示长度为 \(i\) ,ASCII码之和为 \(j\) 的方案数

可得一个显然的转移方程:

\[dp_{i,j}=\sum_{k=\min(1,j-26)}^{26}dp_{i-1,j-k} \]

而且,最最最重要的是我们得到的 \(dp\) 数组是可以预处理出来的,不用每次查询现转移。

小细节:因为题目要求的是一共有多少种与它意思一致的单词,所以要减去它本身,即 \(-1\)

Code

#include<bits/stdc++.h>

using namespace std;
const int mod=1e9+7;
int dp[110][3010],t,sum;
string s;

int main(){
    scanf("%d",&t);
    for(int i=1;i<=26;i++) dp[1][i]=1;
    for(int i=2;i<=101;i++)
        for(int j=i;j<=26*i;j++)
            for(int k=max(1,j-26);k<=j-1;k++)
                dp[i][j]=(dp[i][j]+dp[i-1][k])%mod;
    while(t--){
        cin>>s;
        sum=0;
        for(int i=0;i<s.size();i++) sum+=s[i]-'a'+1;
        printf("%d\n",(dp[s.size()][sum]-1)%mod);
    }
    return 0;
}
posted @ 2020-10-25 11:09  jasony_sam  阅读(55)  评论(0编辑  收藏  举报