【DP】CF156C

一开始想了一个复杂度爆炸的 DP

分析

首先考察题目的性质:

方便起见,将字符看成是 \([0,25]\) 的值。

注意到操作可以等价于选择任意两个下标,然后对应的两个值一加一减或者一减一加。

这样的操作显然不会改变字符串的值和(也就是字符串中每个字符对应的值的和)

进一步地,可以发现答案就是与当前字符串值和(记为 \(val\))相等的字符串个数(当然因为当前字符串本身不计入,所以答案即为所有合法且值和为 \(val\) 的字符串个数 - 1)

这里合法字符串就是每个字符为小写字符的字符串。

考虑 DP 求解:

记长 \(i\) 个字符,值和为 \(j\) 的合法字符串有 \(f(i, j)\) 个。

那么有转移:\(f(i, j) = \sum_c f(i-1, j-c)\),其中 \(c\in [0, 25]\)\(j-c \geq 0\)

// Problem: Cipher
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/CF156C
// Memory Limit: 250 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;
 
#define debug(x) cerr << #x << ": " << (x) << endl
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define dwn(i,a,b) for(int i=(a);i>=(b);i--)
#define pb push_back
#define all(x) (x).begin(), (x).end()
 
#define x first
#define y second
using pii = pair<int, int>;
using ll = long long;
 
inline void read(int &x){
    int s=0; x=1;
    char ch=getchar();
    while(ch<'0' || ch>'9') {if(ch=='-')x=-1;ch=getchar();}
    while(ch>='0' && ch<='9') s=(s<<3)+(s<<1)+ch-'0',ch=getchar();
    x*=s;
}

const int N=110, mod=1e9+7;

int f[N][2605];

int add(int x, int y){
	return x+y>=mod? x+y-mod: x+y<0? x+y+mod: x+y;
}

void init(){
	f[0][0]=1;
	rep(i, 1, 100){
		rep(j, 0, 2600){
			rep(c, 0, 25){
				if(j-c>=0) f[i][j]=add(f[i][j], f[i-1][j-c]);
			}
		}
	}
}

void solve(){
	string s; cin>>s;
	int n=s.size();
	int val=0;
	for(auto i: s) val+=(i-'a');
	cout<<add(f[n][val], -1)<<endl;
}

signed main(){
	init();
	int cs; cin>>cs;
	while(cs--) solve();
	return 0;
}
posted @ 2023-02-24 13:03  HinanawiTenshi  阅读(55)  评论(0编辑  收藏  举报