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