注意:本文的代码可以AC,但解释可能有误,思路仅供参考
感谢zzkgg的修正
Description
Cly成天说自己是个cdd,但这显然是假的,我们都知道clytxdy,为了验证cly到底是cdd还是txdy,请你破解以下问题。
给你一个字符串,请检查其中子序列clycdd
和clytxdy
出现次数,输出出现次数多的那个,如果出现次数一样,那当然是输出clytxdy
,为了方便计算,你只需要比较出现次数对998244353
取余后的结果。
Input
读入一行字符串s
(1<=|s|<=1e5)
Output
输出clycdd
或clytxdy
Sample Input
【样例1输入】
clycddcddtxdy
【样例2输入】
clytxdytxdycdd
Sample Output
【样例1输出】
clycdd
【样例2输出】
clytxdy
本题采用动态规划(DP)的方法
子串和子序列有区别,子串在母串中必须是连续出现的,而子序列没有限制。为方便叙述(我懒),在本文中不作区分
(1) 定义dp[]
的含义
dp[j]
定义为子串(clycdd
或 clytxdy
)中前 j-1
位在母串(输入的s
)中出现的次数。
设s1="clycdd"
,那么dp[6]
为s1
在s
中出现的次数。
(2) 找出dp[]
间的关系式
在外循环,对母串s
从前往后 遍历每一个字符,循环变量为i
在内循环,对子串s1
或s2
从后往前 遍历每一个字符,循环变量为j
对于子串s1[j]
,假设母串中已出现s1[j]的前缀(例如s1="abcb"
,s1[2]
的前缀是"ab"
),此时在母串s中找到了前缀字符串后面应该接上的s1[j]
,那么dp[j]=dp[j-1]
对于相同的j
,如果找到了多个,自然也要算上,所以dp[j]=dp[j-1]+dp[j]
注意题目中还要对答案取模。
for(int i=0;i<lens;i++){
for(int j=lens1-1;j>=0;j--){
if(s[i]==s1[j]){
dp[j+1]=(dp[j+1]+dp[j])%998244353
}
}
}
(3) 找出初始条件
当j=0
时,对应的s1[j]
是字串s1
的开头。在这里,规定dp[0]=1
。可以近似理解为s1[0]
前面已找到前缀(空字符串""
)。
此时如果在母串s
中找到m
个s[0]
,那么dp[1]=m
(dp[0]=1
和dp[1]=m
应该是等价的)
#include<iostream>
using namespace std;
string s,s1="clycdd",s2="clytxdy";
long long dp1[10],dp2[10];
int main(){
cin>>s;
int lens=s.length();
int lens1=s1.length(),lens2=s2.length();
dp1[0]=1;dp2[0]=1;
for(int i=0;i<lens;i++){
for(int j=lens1-1;j>=0;j--){
if(s[i]==s1[j]){
dp1[j+1]=(dp1[j+1]+dp1[j])%998244353;
}
}
for(int j=lens2-1;j>=0;j--){
if(s[i]==s2[j]){
dp2[j+1]=(dp2[j+1]+dp2[j])%998244353;
}
}
}
// cout<<dp1[lens1]<<endl<<dp2[lens2];
if(dp1[lens1]<dp2[lens2]) cout<<"clytxdy";
else cout<<"clycdd";
return 0;
}