F - Unhappy Hacking
问题描述
Sig 制作了自己的键盘。设计的极简风格,这个键盘上只有 3 个按键:按键 0
、按键 1
和退格键。
一开始,他在使用一个普通文本编辑器与这个键盘配合。这个编辑器总是显示一个字符串(可能为空)。在编辑器启动后,这个字符串是空的。当按下键盘上的每个按键时,字符串会发生以下变化:
- 按键
0
:会在字符串右侧插入一个字母0
。 - 按键
1
:会在字符串右侧插入一个字母1
。 - 退格键:如果字符串为空,则不会发生任何变化。否则,会删除字符串最右侧的一个字母。
Sig 启动了编辑器,并总共按下这些按键 𝑁 次。结果,编辑器显示的字符串为 𝑠。找出按下按键的方式数量,对 1e9+7 取模后的结果。
约束条件
s 由字母 0
和 1
组成。
输入
输入以以下格式从标准输入给出:
N
s
输出
输出按下按键 𝑁 次总共的方式数量,使得编辑器最终显示字符串 𝑠,对 1e9+7 取模后的结果。
示例 1
Input
3
0
Output
5
我们用 B
表示退格键。以下 5 种按键方式会导致编辑器最终显示字符串 0
: 00B
、01B
、0B0
、1B0
、BB0
。在最后一种方式中,按下退格键时不会发生任何变化。
示例 2
Input
300
1100100
Output
519054663
示例 3
Input
5000
01000001011101000100001101101111011001000110010101110010000
Output
500886057
题解
对于此题考虑二维动态规划。
定义
定义dp[i][j]表示第i次操作后,剩下j个字符的方案数。
转移
考虑刷表,当前第i+1次操作有两种可能:
1.按键0/按键1:加了一个字符,更新dp[i][j],因为两种情况,所以要乘2
2.退格键:此时我们删除的是第j位的字符,因为可能没有字符,所以要与0取最大值。因为我 们是删除一个字符,所以可以不用讨论是1还是0的情况,因此不用乘2。
答案
原本是dp[n][s.size()],但是我们注意到一个问题:我们只是考虑了剩下n.size()位,并没有考虑 每一位上是0/1,所以我们需要除以pow(2,n.size()),但因为这里要取模,所以要用快速幂。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 5005;
const int mod = 1e9 + 7;
char s[MAXN];
ll dp[MAXN][MAXN];
ll p(ll x, ll y)
{
ll sum = 1;
while (y != 0)
{
if (y & 1)
sum = sum * x % mod;
x = x * x % mod;
y >>= 1;
}
return sum % mod;
}
int main()
{
int n;
scanf("%d%s", &n, s + 1);
int m = strlen(s + 1);
dp[0][0] = 1;
for (int i = 0; i <= n; i++)
for (int j = 0; j <= i; j++)
{
dp[i + 1][j + 1] = (dp[i + 1][j + 1] + 2 * dp[i][j]) % mod;
dp[i + 1][max(j - 1, 0)] = (dp[i + 1][max(j - 1, 0)] + dp[i][j]) % mod;
}
printf("%lld", dp[n][m] * (p(p(2, m), mod - 2) % mod) % mod);
return 0;
}