题目链接:https://codeforces.com/gym/102875/problem/H
题意:t组数据,每组数据都是由一个长度为n的大串和m个小串与m个一点作用都没有的字符组成的,现在要问你在大串能分解成唯一的小串组合。如果可以输出"happymorsecode",如果不能由小串组成,输出"nonono",如果可以分解成很多个小串组合的情况,输出"puppymousecat"与方案数对128取模。
思路:注意到一个细节,那就是m个小串长度在1~5。那么很容易联想到dp,考虑dp[i] 表示以第i位上字母为最后一个小串最后一个字母的方案数。那么对于每个位置我们最多枚举5次就OK了。但是值得注意的是,如果只考虑方案数,很容易出现某个位置dp[j]=0,致使后面出现一大堆0,影响最后的结果,明明是puppymousecat的情况,结果却输出个nonono出来。这说明我们要找个标记,来看有无解的存在。在枚举过程中,每次都需要查询这个字符串是否存在,很容易想到要用map来维护。至此代码也就写出来。
//team yglance+xhwl+TTD //author: CN.TTDragon #include<bits/stdc++.h> typedef long long ll; const ll mod=128; const ll maxn=1e5+7; const double pi=acos(-1); using namespace std; int t,n,m; char ch;//那个没用的字符 string s; int len; string ss; ll dp[maxn];//记录方案数 ll have[maxn];//1表示只有一个解,0表示确实没有,2表示多个 map<string , ll > MP;//记录每个小串出现的次数 int main() { ios::sync_with_stdio(false); //freopen("in.in","r",stdin); //freopen("mine.out","w",stdout); cin>>t; while(t--) { //初始化 MP.clear(); memset(dp,0,sizeof(dp)); memset(have,0,sizeof(have)); cin>>n>>m; for(int i=0;i<m;i++) { cin>>ch>>s; MP[s]++;//对应小串的数量+1 } cin>>s;//接收大串 len=s.length(); s=" "+s;//一开始没有在前面加这个空格,发现操作起来巨麻烦,以至于面向BUG编程了 dp[0]=1; have[0]=1;//这两为什么置1,看完下面就明白 for(int i=1;i<=len;i++) { ss=""; for(int j=i;i-j<=4&&j>=1;j--) { ss=s[j]+ss;//这个逆序,秀烂我的狗头 if(MP[ss])//说明这个串是存在的 { dp[i]+=dp[j-1]*MP[ss];//以这个位置结尾的情况数量增加,ss串是s[i]s[i+1]……s[j] 这个dp[i]需要增加的数量就是dp[j-1]与ss数量的乘积。 have[i]+=have[j-1]*MP[ss];//有无解的标记也是同理 have[i]=min(have[i],2ll);//注意的是,我们用了0,1,2三种情况表示三种解的情况,这里放回来就好,没必要过大。(关键是自己舒服) dp[i]%=mod; } } } //值得注意的是dp[len]=0,不一定代表没有解,这正是have存在的意义 if(have[len]==0)//最后一位根本不存在解的情况,nonono { cout<<"nonono"<<endl; } else if(have[len]==1)//恰好最后一位只有一种情况 { cout<<"happymorsecode"<<endl; } else//多种情况 { cout<<"puppymousecat "<<dp[len]<<endl; } } return 0; }
写在最后:看到第一眼,心想字典树,终于能给队伍做贡献了,结果就是一小时时间从T2到T3再到自闭。
雨淋湿了天空 毁的很讲究。