codeforces-#667-F

codeforces-#667-F

九月 16, 2020

F. Subsequences of Length Two

题意

给出一个长为n的串s(n<200)和一个只有两个字符的串t

一次修改可以将s串中的某个字符变成任意其他字符

要求在不超过k次修改的情况下 使s中等于t的子序列最多

题解

n<200可以考虑$n^3$ 的dp

最初想到dp(pos,修改成t[0]的次数,修改成t[1]的次数) dp值表示以pos未结尾有多少个子序列=t

后来根据题解设dp(pos,修改次数,到pos前s中有多少个字符=t[0])

同样dp值表示以pos未结尾有多少个子序列=t;

那么存在三种情况下的状态转移

e0=s[i]==t[0];

e1=s[i]==t[1];

  • 不修改:dp(i+1,c,cnt+e0)=max(self,dp(i,c,cnt)+e1?cnt:0);
  • 修改成t[0]:dp(i+1,c+1,cnt+1)=max(self,dp(i,c,cnt));
  • 修改成t[1]:dp(i+1,c+1,cnt)=max(self,dp(i,c,cnt)+cnt);

可以将t[0]==t[1]的情况单独考虑 也可以直接在转移中处理

先将dp初始化为-inf 无法转移到的状态直接跳过

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define endl '\n'
#define Turnoff std::ios::sync_with_stdio(false)
const ll Max=1e18+5;
const ll Mod=998857459;
/*

*/
ll dp[205][205][205];///pos,changetime,num of t[1]
int main() {
Turnoff;
int n,k;cin>>n>>k;
string s,t;cin>>s>>t;
ll ans=0;
if(t[0]==t[1]){
ll cnt=0;
for(int i=0;i<n;i++){
if(k&&s[i]!=t[0])cnt++,k--;
else if(s[i]==t[0])cnt++;
}
ans=cnt*(cnt-1)/2;
}
else{
for(int i=0;i<=n;i++)for(int j=0;j<=n;j++)for(int k=0;k<=n;k++)dp[i][j][k]=-Max;
dp[0][0][0]=0;///dp第一维从1到n 有实际意义
for(int i=0;i<n;i++){
for(int c=0;c<=min(k,i);c++){
for(int cnt=0;cnt<=i;cnt++){
if(dp[i][c][cnt]<0)continue;
///此处若不将非法点跳过可能会把 cnt>修改次数+s[i]==t[0]个数的情况错误更新
int e0=s[i]==t[0],e1=s[i]==t[1];
dp[i+1][c][cnt+e0]=max(dp[i+1][c][cnt+e0],dp[i][c][cnt]+(e1?cnt:0));
if(c<k){
dp[i+1][c+1][cnt+1]=max(dp[i+1][c+1][cnt+1],dp[i][c][cnt]);
dp[i+1][c+1][cnt]=max(dp[i+1][c+1][cnt],dp[i][c][cnt]+cnt);
}
}
}
}
for(int c=0;c<=k;c++){
for(int cnt=0;cnt<=n;cnt++)ans=max(dp[n][c][cnt],ans);
}
}
cout<<ans<<endl;
}