[nh云课堂]周中测试

T1

给一个长度是 nn 的仅含有小写字母的字符串ss,求 ss 的不同非空子序列一共有多少个,答案对 10000000071000000007 取模。

下面给出子序列定义:

字符串的子序列是经由原字符串删除 kk 个(kk 有可能等于 00)字符但不改变剩余字符相对位置的一个新字符串。

1n20001\leq n\leq 2000


思路

考虑 dp。定义 dpidp_i 为以第 ii 个字符为结尾的不重复子序列数量。如果将 dpi=i=1i1dpidp_i=\sum\limits_{i=1}^{i-1}dp_i。就会出现重复的情况。

例如 ABAB\tt{ABAB}a1,a2a_1,a_2a3,a4a_3,a_4 都是 AB\tt{AB}。就会被讨论两次,这是错误的。


现在来考虑正确的状态转移方程,dpidp_i 可以从 dpjj=1i1dp_{j}|j=1\dots i-1 转移过来。

考虑转移的方式,考虑到状态不重复的定义。既然如此,直接将原序列的末尾替换(删掉再加上)掉就可以了,此时如果 si=sjs_i=s_j ,替换后还是原来的序列,不能转移。

需要注意的是,我们还可以添加 sjs_j,组成 s1sjs_1\sim s_j。这样的序列一定合法。

不能添加 jj

例如 ABCD\tt{ABCD}。如果将 AB\tt{AB} 加上 D\tt{D},和 ABC\tt{ABC} 替换 D\tt{D} 重复了。而 ABC\tt{ABC} 加上 D\tt{D} 是不会重复的,因为原本没有一个长度为 44 的序列。


综上所述,状态转移方程为:

dpi=dpjj=1...i1&sjsidp_i=dp_j|j=1...i-1 \And s_j \neq s_i

实现

我们可以将 dp01dp_0\gets 1,在转移时,就可以实现添加的操作。


代码

#include<bits/stdc++.h>
using namespace std;
const int N = 2e3+10,Mod=1e9+7;
char ch[N];
int dp[N]={1},ans;
int main() {
	cin>>ch+1;
	int n=strlen(ch+1);
	for(int i=1;i<=n;i++) {
		for(int j=0;j<i;j++)
			if(ch[i]!=ch[j]) dp[i]=(dp[i]+dp[j])%Mod;
		ans=(ans+dp[i])%Mod;
	}
	cout<<ans;
	return 0;
}

T2

组合数问题

前置知识:杨辉三角。

由于 kk 已经确定。

posted @ 2023-09-25 13:52  cjrqwq  阅读(8)  评论(0)    收藏  举报  来源