codeforces 597 div2 Constanze's Machine(斐波拉契数列 DP)

题目大意:

有一个打字机,每次输入m会输出nn,输入w会输出uu。

现在我们有一个字符串,问没经过打字机的原字符串的有多少种。假如不存在可以输出0。

解题思路:

首先我们必须能够反应到答案是连续u和连续n的对应的种类数的连乘。例如:

字符串 nnaaaabbbbuuu那么答案只可能是由连续n或者连续u贡献的。那么这里就想到可以先打个表,这个表F[i]代表u或者n连续出现i个时字符串数的可能,然后判断有多少个连续u或者连续n然后我们把表里面的内容连乘即可。

关键是这个表怎么递推。我们也可以想到思路无非两种。一种是用排列组合的思想。这里用不了。那么我们可以从DP的角度考虑。

我们再定义一个memo表,memo[i][j],其中i代表连续出现第i个,j是0或者1代表当前我们是把这个字符当作原字符还是改变为另一个,意思是例如:uu的第二个u我们既可以看作u也可以看作和前面结合成为W。当字符串长度为3或者以上时,我们有如下递推:

\\memo[i][1]=F[i-1]-memo[i-1][1]\\ memo[i][0]=F[i-1]\\ F[i]=memo[i][1]+memo[i][0]

第一个公式是因为:我们当前假若把字符当作W,那么前一个字符就不能当作W,所以我们需要用前一个字符的合理数再减去前一个字符当W的方案数。

第二个公式是:我们可以把当前字符合理数等于前一个字符的总合理的方案数。这里不用关心前面是u还是W。

第三个公式就是更新F。表明当前方案合理数。

memo[1][0]=1; memo[2][0]=1; memo[2][1]=1; F[2]=2;

初始化完成。

废话:注意这里我们要用到减后取模,注意这里先加上MODN再取。

另外这里不同于一般的递推公式,这里引入了一个memo,这里是比较新颖的地方。还有,这题其实递推的规律是斐波拉契,但是我看不出来,所以这里提供了另外一种思路。

假如你是访客请无视这句话:另外,我们要清楚返回的是区间长度还是返回长度减1。若是区间长度,我们返回的是r-l+1否则是r-l。另外,我们在静态调试的时候,重点看数组的定义域和值域范围是否正确,比如是不是从0开始。结束多少。假如定义域和值域都检查不到问题,再对照题目想思路。假如还是不知道为啥WA,可以多试试几组样例。

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN=1e5+10;
const int MODN=1e9+7;
int memo[MAXN][2];
int F[MAXN];
int32_t main(){
	string str;cin>>str;
	memo[0][0]=1;
	memo[1][0]=1;
	memo[1][1]=1;
	F[0]=1;
	F[1]=2;
	for(int i=2;i<MAXN;i++){
		memo[i][1]=(F[i-1]-memo[i-1][1]+MODN)%MODN;
		memo[i][0]=(F[i-1])%MODN;
		F[i]=(memo[i][0]+memo[i][1])%MODN;
	}
	int i,j;
	int n=str.size();
	int ans=1;
	for( i=0, j=0;i<n;i=j){
		if(str[i]=='m'||str[i]=='w'){
			cout<<0<<endl;
			return 0;
		}
		j=i;
		while(j<n && str[j]==str[i]){
			j++;
		}
		if(str[i]=='u' || str[i]=='n'){
			ans*=F[j-i-1];
			ans%=MODN;
		}
	}
	cout<<ans<<endl;
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值