1. 程式人生 > 實用技巧 >CodeForces Round #667(Div.3) F 略解

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