键帽(dp)
题目描述
部分键帽缺乏职业道德。Nyamu 是网络知名键盘侠,她在敲键盘时发现:
如果一个只由小写字母组成的字符串中不存在超过 k 个元音相邻,那么这个字符串就是好串。Nyamu 想知道长度为 n 的字符串中,有多少个好串。由于这个答案可能很大,因此你需要将答案对 109+7 取模。元音共有5个,分别为 'a' , 'i' , 'u' , 'e' , 'o' ,其他字母都为辅音。
输入
第一行输入一个正整数 T(1≤T≤2×105) ,表示数据组数。
对于每一组数据:在一行中输入两个正整数 n,k(1≤k≤n≤106) ,表示字符串长度,好串定义。数据保证 ∑n≤5×106。
输出
对于每组数据,在一行中输出一个整数表示答案。
样例输入
1
2 1样例输出
651
提示
第一个字符为元音,第二个字符为辅音的字符串有105种。
第一个字符为辅音,第二个字符为元音的字符串有105种。
第一个字符为辅音,第二个字符为辅音的字符串有441种。
105+105+441=651。
思路:动态规划。用一个dp数组表示长度为i的字符串中好串的数量。分三种情况
- i<=k,没有限制,每个位置都可以填任意字符, 转移方程:
- i==k+1,用总的-不符合的,总的算法跟1一样,不符合的:全是元音5^(k+1), 转移方程:
- i>k,跟二类似也是要减去不符合的,这时可以将这个长度为i的分成三个部分i-k-2的好串+1个辅音(保证i-1还是个好串)+k+1个元音 转移方程:
一些细节提醒:在计算时,会有溢出风险,乘了两个数就要%mod
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
const int N=1000010;
const int mod=1000000007;
ll pow5[N];
ll dp[N];//长度为i的字符串中好串的数量
int n,k;
void solve(){cin>>n>>k;dp[0]=1;for (int i=1;i<=n;i++){if (i<=k)dp[i]=(dp[i-1]*26)%mod;else if (i==k+1){dp[i]=(dp[i-1]*26-pow5[k+1])%mod;if (dp[i]<0)dp[i]+=mod;}else{ll t=21*dp[i-k-2]%mod*pow5[k+1]%mod;dp[i]=(dp[i-1]*26-t)%mod;if (dp[i]<0)dp[i]+=mod;}}cout<<dp[n]<<"\n";return ;
}
int main() {ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);pow5[0]=1; for (int i=1;i<=1000001;i++){pow5[i]=(pow5[i-1]*5)%mod;}int T;cin>>T;while(T--){solve();}
}