CodeForces Round #667(Div.3) F 略解
暑假忙着在打多校和牛客 后期还在学matlab和python为水数模做准备 疏于补题 加上自己懒 所以博客又鸽了好久
今天来讲一讲这个Div3的F
一、题目大意
给定一个长度为n的小写字母串s和另一个长度为2的小写字母串t.
我们可以对s串进行如下操作:把s串中的任意一个字符替换成a-z之间的任何一个字符.
给定最大操作次数k,求在k次操作内能够获得的s中,包含最多多少个等于t的子序列?输出子序列个数。
数据范围n<=200,k<=n.
二、样例分析
我们先约定,s的下标是从1开始的,t的下标是从0开始的。
翻译的有点抽象(
我们通过一个样例来看
【样例输入】
n=4 k=2
s="bbaa" t="ab"
【样例分析】
经过2次操作,我们可以把s串变成 “abab" ,它里面包含3个等于t的子序列,分别是(s[1],s[2]) ; (s[1],s[4]) 与(s[3],s[4])
可以证明3是我们所能得到的最大值,所以答案为3.
【样例输出】
3
三、解题思路
显然这道题是一个可以O(n^3)莽过去的题 (甚至还有O(n^4)的冲过去了).
对于这种类型的题,我们可以考虑dp.
令dp[i][j][k]表示前i个字符中,已经操作了j次,含有k个t[0]个字符的这个串包含子序列t的个数。
比如,对于上面的样例,dp[2][0][0]=0; 因为我一次都没操作,前两个字符是bb,包含0个t[0](即a),这样的串是不包括t的,所以dp[2][0][0]=0.
(当然从上面的例子也可以看出来有些状态是不存在的)
那我们就要分成以下几种情况来考虑了:
【第一种情况——当前的字符s[i]就是t[1]】
我们可以不对其进行修改,那多出来的次数就是长度i-1的字符串中t[0]的个数了。即:
dp[i][j][k]=max(dp[i][j][k],dp[i-1][j][k]+k*(s[i]==t[1]))
特殊地,当前面已经有t[0]的情况下,如果t[1]=t[0]的话,则有:
dp[i][j][k]=max(dp[i][j][k],dp[i-1][j][k-1]+(k-1)*(s[i]==t[1]))
理解起来和上面是一样的。
【第二种情况——把当前字符改成t[0]】
这一步要求修改次数还有剩余,理解起来也和上面的一样。即:
dp[i][j][k]=max(dp[i][j][k],dp[i-1][j-1][k-1]+(k-1)*(t[0]==t[1]))
【第三种情况——把当前字符改成t[1]】
没啥好说的,就是多了k个t. (因为前面有k个t[0])当然这一步也要求我们要有剩余的修改次数。即:
dp[i][j][k]=max(dp[i][j][k],dp[i-1][j-1][k]+k)
然后把所有的dp[i][j][k]跑一遍求个最大值就行了。
代码里面也有一些注释,可以参考着也看看qwq
四、AC代码
#include<bits/stdc++.h> using namespace std; #define fast ios::sync_with_stdio(0), cin.tie(0), cout.tie(0) #define mem(x,y) memset(x,y,sizeof(x)) #define ll long long #define pii pair<int,int> #define pll pair<ll,ll> #define mp make_pair #define pb push_back #define db double #define inf 0x3f3f3f3f bool ispow(ll n){return (n&(n-1))==0;} #define lowbit(x) (x&(-x)) const int mod=1e9+7; int n,lim; string s,t; int dp[205][205][205];//first i characters; j modifications done; has k t[0]s. int main() { fast; cin>>n>>lim; cin>>s>>t; s=' '+s; mem(dp,-0x3f); dp[0][0][0]=0; for(int i=1;i<=n;i++){ for(int j=0;j<=lim;j++){ for(int k=0;k<=n;k++){ dp[i][j][k]=max(dp[i][j][k],dp[i-1][j][k]+k*(s[i]==t[1]));// current character is t[1] if(k>0&&(s[i]==t[0])){ dp[i][j][k]=max(dp[i][j][k],dp[i-1][j][k-1]+(k-1)*(s[i]==t[1]));//same as above. } if(j>0&&k>0){ dp[i][j][k]=max(dp[i][j][k],dp[i-1][j-1][k-1]+(k-1)*(t[0]==t[1]));//change current character to t[0] } if(j>0) dp[i][j][k]=max(dp[i][j][k],dp[i-1][j-1][k]+k);//change current character to t[1] } } } int ans=-19260817; for(int i=1;i<=n;i++) for(int j=0;j<=lim;j++) for(int k=1;k<=n;k++) ans=max(ans,dp[i][j][k]);//find the answer. cout<<ans<<'\n'; return 0; }
五、总结
稍微总结一下,这道题是一个没有那么难想的dp,在做的时候感觉和编辑距离有一点点相同的地方。
本篇题解和官方题解的做法其实差不多,当然,官方还给出了一个O(n^4)的暴力供大家欣赏
链接:https://codeforces.com/blog/entry/82284

浙公网安备 33010602011771号