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;
}
View Code

 

五、总结

稍微总结一下,这道题是一个没有那么难想的dp,在做的时候感觉和编辑距离有一点点相同的地方。

本篇题解和官方题解的做法其实差不多,当然,官方还给出了一个O(n^4)的暴力供大家欣赏

链接:https://codeforces.com/blog/entry/82284

 

posted @ 2020-09-17 20:24  Doomfist  阅读(190)  评论(0)    收藏  举报