周赛F-诺基亚(hard version)
比赛题目
内存限制:C/C++ 256MB,其他语言 512MB
描述
今天小c学长买盲盒中了一个诺基亚,于是他对着这诺基亚研究了起来,他突发奇想想到了一种玩法。我们都知道诺基亚的一个按键可以按出多个字母,于是小c学长规定一个按键上的第几个字母就需要按几次那个数字才能出现(例如2上的字母有a,b,c,我们要a就要按1次2,b要按2次2,c要按3次2)。
小c学长觉得之前的玩法不够有趣,于是他换了一种玩法,将条件和答案进行了颠倒,但是发现颠倒后的答案就不唯一了,于是他希望知道这个按键方式到底能代表多少中字符串。(答案可能有点大,请模1e9+7后输出)
输入描述
一个整数t,代表有t组样例
每组样例第一行一个n,代表按键字符串长度
第二行n个字符,代表小c的按键顺序。
(保证所有的n加起来不超过1e5,且字符串只包含’2’-‘9’)
输出描述
每组样例输出一整数,表示这个按键方式对应有多少种字符串。
(请模1e9+7后输出)
用例输入 1
3 5 22233 3 222 3 232
用例输出 1
8 4 1
用例输入 2
1 12 222222222222
用例输出 2
927
提示
第一组样例解释,可能的字符串有"aaadd", “abdd”, “badd”, “cdd”, “aaae”, “abe”, “bae” 和 “ce” ,所以答案是8.
这是一个很痛苦的题目,对于新手而言。需要用到一点暴力跑程序去找规律,不难发现,除了7和9以外,剩下的数字都只对应了3个字母,:
输入字母 a :按键 2
输入字母 b :按键 22
输入字母 c :按键 222
而按键数字表示的字母可能性却很多:
按键 2 可能表示:a 共 1 种
按键 22 可能表示:aa, b 共2种
按键 222 可能表示:aaa, ba, ab, c 共 4 种
不难发现,这是一个爬楼梯增强版(其实也可以通过写一个暴力程序跑出规律得出结论),当前数字和上一个数字相同的时候,到达当前位置的方案数可以由上个位置和上上个位置转移过来,那么可以得出递归式子:f(i)=f(i−1)+f(i−2)+f(i−3),[i>3],那么7和9也是相同的规律,得出f(i)=f(i−1)+f(i−2)+f(i−3)+f(i-4),[i>4],因为字符串长度最长1e5,我们可以预处理出两个递归式的前1e5的值的和,那么开始遍历字符串,先初始化一个ans为1,然后每次出连续相同字符的长度,然后将预处理对应的值乘进ans里。以上来自我们学长。
AC代码:
#include<bits/stdc++.h> using namespace std; const int mod=1e9+7; const int N=2e5+10; typedef long long ll; #define endl "\n" ll unusual[N],usual[N]; int main(){ int t; cin>>t; usual[1]=1,usual[2]=2;usual[3]=4;//注意别放在while里面了哦,我就直接wa了9次才发现我是一个大怨种。。。 for(int i=4;i<=100001;i++) usual[i]=(usual[i-1]+usual[i-2]+usual[i-3])%mod; unusual[1]=1,unusual[2]=2,unusual[3]=4,unusual[4]=8; for(int i=5;i<=100001;i++) unusual[i]=(unusual[i-1]+unusual[i-2]+unusual[i-3]+unusual[i-4])%mod; while(t--){ ll n; cin>>n; string str; cin>>str; ll ans=1; for(int i=0;i<str.length();){ ll num=i,sum=0,temp; while(num<str.length()&&str[i]==str[num]){ num++; sum++; } if(str[i]=='7'||str[i]=='9')temp=unusual[sum]; else temp=usual[sum]; i=num; ans=(ans*temp)%mod; } cout<<ans<<endl; } return 0; }
浙公网安备 33010602011771号