A. 【美团杯2020】查查查乐乐
“查查查乐乐”是一段古老神秘的咒语,只有被选中的魔法师才有资格使用这一段咒语并享用它所带来的力量;而如果这段咒语出现在了不具资格的魔法师的口中,这个魔法师将会遭到咒语的反噬并付出可怕的代价。
这个学期,镁团在一家魔法早教学校做兼职,他的任务是教小学生们魔法并帮助他们准备一年一度的全国魔法奥林匹克竞赛 (NOMP)。今天,镁团在整理图书的时候,突然发现一本课外教材中包含了 t 段只由查和乐组成的咒语。让小学生们阅读这些咒语是非常危险的:他们可能会在无意识中念出“查查查乐乐”。
因此,作为一名富有责任心的儿童教师,镁团打算修改这些咒语,从而最大程度地杜绝这方面的隐患。镁团认为一段由查和乐组成的咒语是危险的当且仅当在删去咒语中的若干个字(也可以不删)后,剩下的咒语可能变成查查查乐乐。举例来说,“查查查乐乐”,“查查乐查乐乐” 就是危险的,而 “乐乐查查查”,“乐查乐乐查乐查查”就不是危险的。
对于每一段咒语,镁团都可以选择若干个位置并对这些位置进行修改:他可以把“查”变成“乐”,也可以把“乐”变成“查”。为了最大限度地保留教学效果,镁团希望使用尽可能少的修改来消除所有的危险性:对于每一段咒语,镁团都希望你帮他计算一下最少的修改次数。
输入格式
输入第一行是一个整数 t(1≤t≤1000),表示咒语的数量。
对于每组数据,输入包含一行一个只包含字符 x 和 l 的字符串 s(1≤|s|≤100),描述了一段咒语。其中 x 表示“查”,l 表示 “乐”。
输出格式
对于每段咒语,输出一行一个整数表示最少的修改次数。
样例一
input
3
xxxll
xxlxllllxl
xxxxxlllll
output
1
1
3
explanation
对于三段咒语,我们分别给出一种让修改次数最小的方案:
xxlll,修改了第 3 个字。
xxllllllxl,修改了第 4 个字。
xxllllllll, 修改了第 3,4,5 个字。
限制与约定
Small Task: n≤10
Large Task: n≤100。
时间限制:2s
空间限制:512MB
思路:
题目的意思是给我们一个序列,让我们要修改一些字符,使这个序列不存在xxxll的子序列
我们定义一个dp数组表示当前的修改次数
dp[1]表示不存在x序列的修改次数
dp[2]表示不存在xx序列的修改次数
dp[3]表示不存在xxx序列的修改次数
dp[4]表示不存在xxxl序列的修改次数
dp[5]表示不存在xxxll序列的修改次数
这样dp数组的初始值都为0
我们在遇到x的时候,不能出现的序列是x,xx,xxx,这时我们可以修改当前的x,也可以修改前i-1个的x,这样都不能构成xxxll序列,修改次数就有dp[1] = dp[1] + 1,由于要求最小值,所以dp[2] = min(dp[1],dp[2] + 1),同理dp[3] = min(dp[2],dp[3] + 1)
我们在遇到l的时候,前面不能出现的序列是xxxl和xxxll,同理可以修改当前的l,也可以修改l之前的x,所以dp[4] = min(dp[3],dp[4] + 1),同理dp[5] = min(dp[4],dp[5] + 1)
这样dp方程就出来了,只需要在for循环中判断当前的字符是x还是l即可
int dp[6];
char s[101];
int main(){
int T; scanf("%d",&T);
while(T--){
dp[1] = dp[2] = dp[3] = dp[4] = dp[5] = 0; // dp数组初始化
scanf("%s",s+1);
for(int i=1;s[i];i++){
if(s[i] == 'x'){
dp[3] = min(dp[2], dp[3] + 1);
dp[2] = min(dp[1], dp[2] + 1);
dp[1] = dp[1] + 1;
}else{
dp[5] = min(dp[4], dp[5] + 1);
dp[4] = min(dp[3], dp[4] + 1);
}
}
printf("%d\n",dp[5]);
}
return 0;
}