nkoj构串

题目描述

用0和1构造一个长度为 $n$ 的数字串,要求串中不能出现 101 和 111,问能构造出多少个符合条件的串?

比如,当  $n = 3$ 时,可构出下列满足条件的串:
000
001
010
011
100
110

输入格式

第一行一个正整数 $n$

输出格式

输出一个整数表示答案,结果可能很大,mod 1000000007后输出(mod的意思是求余,相当于"%")。

样例

样例一输入

5

样例一输出

15

 样例二输入

9999

 样例二输出

382276560

提示

$1 \le n \le 1000000$

注意:结果可能会大到超过 long long 的范围,所以题目要求与1000000007取余后再输出结果 

思路

划分阶段:

使用 dp 算法,记录前两位数。

状态设计:

 $f[i][0][0]$ 表示第i位前一位为0,添加的一位为0;

 $f[i][0][1]$ 表示第i位前一位为0,添加的一位为1;

$f[i][1][0]$ 表示第i位前一位为1,添加的一位为0;

$f[i][1][1]$ 表示第i位前一位为1,添加的一位为1。

转移方程:

  1. 前两位为0,0。$f[i][0][0] = f[i - 1][0][0] + f[i - 1][1][0]$
  2. 前两位为0,1。$f[i][0][1] = f[i - 1][0][0]$;(因为 101 不能出现)
  3. 前两位为1,0。$f[i][1][0] = f[i - 1][0][1] + f[i - 1][1][1]$
  4. 前两位为1,1。$f[i][1][1] = f[i - 1][0][1]$;(因为 111 不能出现)

边界条件:

当只有1位时,但是记录的是前2位。

我们有一下两种解决方法:

  1. 直接记录两位的情况,从第3位开始循环,如果输入1位就特判一下;
  2. 观察到 101 和 111 都以 1 开头,也就是说开头为 0 的没有限制。我们可以把1位当作开头为 0 来 dp,就没有问题了。

 第1种方法具有普遍性,第2种方法要看限制的字符串的规律。

代码

#include<bits/stdc++.h>
using namespace std;
int n;
const int mod = 1000000007;
long long a[1000010][2][2] = {0};
int main(){
	scanf("%d", &n);
	a[1][0][0] = 1;
	a[1][0][1] = 1;
	for(int i = 2; i <= n; ++ i){
		a[i][0][0] = a[i - 1][0][0] + a[i - 1][1][0];
		a[i][0][1] = a[i - 1][0][0];
		a[i][1][0] = a[i - 1][0][1] + a[i - 1][1][1];
		a[i][1][1] = a[i - 1][0][1];
		a[i][0][0] %= mod;
		a[i][0][1] %= mod;
		a[i][1][0] %= mod;
		a[i][1][1] %= mod;
	}
	printf("%lld", (a[n][0][0] + a[n][0][1] + a[n][1][0] + a[n][1][1]) % mod);
	return 0;
}

  • 13
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值