ZROJ370 Medium Counting - 区间dp -

题目链接:http://zhengruioi.com/problem/370

题解:
考虑对于 \(S[l..r]\) ,如果要符合条件必然是在最高位分成了至少两段(也可能没有分出来,那就继续下一位) \(S[l..k] 和 S[k+1..r]\),其中前半段这一位的字符小于后半段这一位的字符,然后前半段就往下一位递推,后半段还需要重复这个过程,这个过程很像区间dp
\(dp[l][r][p][c]\) 表示考虑 \([l..r]\) 这一段,考虑到 第 \(p\) 位,并且这一位填的字符 \(\geq c\) 的方案数

  • \(dp[l][r][p][c] \leftarrow dp[l][r][p][c+1]\)
  • \(dp[l][r][p][c] \leftarrow dp[l][k][p+1][0] \times dp[k+1][r][p][c+1]\) (前半段下一位开始不存在“这一位填什么”的限制,后半段这一位至少是 \(c+1\) 了)

其中第二个转移必须要保证 \([l..k]\) 都能填 \(c\) 这个字符('?'或者c本身)
边界条件:如果 \(c>26\) ,那么这个状态显然不可以;如果当前需要填的位 \(>maxlen\),那么如果 \(l==r\) 就是1,否则为0(要严格递增)
注意字符串可能不是一样长,在后面补空格(\(c=0\))即可

// by SkyRainWind
#include <bits/stdc++.h>
#define mpr make_pair
#define debug() cerr<<"Yoshino\n"
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
#define pii pair<int,int>

using namespace std;

typedef long long ll;
typedef long long LL;

const int inf = 1e9, INF = 0x3f3f3f3f, mod = 990804011, maxn = 55;

int n;
char s[maxn];
ll dp[maxn][maxn][22][29];
int a[maxn][maxn], ml;

ll dfs(int l,int r,int p,int c){
	ll &dd = dp[l][r][p][c];
	if(~dd)return dd;
	if(c == 27)return dd = 0;
	if(p > ml)return dd = (l == r);
	
	dd = dfs(l, r, p, c+1);
	int gg=0;
	for(int k=l;k<=r-1;k++){
		if((a[k][p] == c) || (a[k][p] == 27 && c))
			(dd += 1ll * dfs(l,k,p+1,0) * dfs(k+1,r,p,c+1)%mod) %= mod;
		else{gg = 1;break;}
	}
	if(!gg && (a[r][p]==c || (a[r][p]==27&&c)))(dd += dfs(l,r,p+1,0)) %= mod;
	return dd;
}

signed main(){
	memset(dp,-1,sizeof dp);
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%s",s + 1);
		int l = strlen(s + 1);
		ml = max(ml, l); 
		for(int j=1;j<=l;j++)
			a[i][j] = s[j] == '?' ? 27 : s[j] - 'a' + 1;
	}
	ll ans = dfs(1, n, 1, 0);
	cout<<ans;

	return 0;
}
posted @ 2023-01-19 11:44  SkyRainWind  阅读(19)  评论(0编辑  收藏  举报