題意:輸入一個S串和一個T串,|S|>= |T|,問最少要修改S中的幾個字母才能使S中有子序列T
思路:dp+貪心
f[i][j]表示以S中第i個字母開頭的串包含T中第j個字母開頭的串所要修改的最少的字母數,
即S中i之前的字母已經包含T中j之前所有的字母,所以分別從i和j位置繼續匹配
過程簡述如下:
S: ABCECDFF
T: BBDEC
開始i=1,j=1
在S[i]開始尋找T[j]即'B',在i=2位置找到,此時我們面臨兩個選擇,要麼就讓i=2和j=1匹配,要麼就修改i=1的'A'爲'B',因爲既然修改肯定就修改最前面的
假如選了前者,接下來就從i=3,j=2繼續匹配,假如選了後者,就從i=2,j=2繼續匹配,無論怎麼選,後面面臨的子問題和剛纔面臨的問題一模一樣
對於這個樣例,選擇後者總共只需要修改2個S中的字母,選擇前者總共需要修改3個S中的字母
本題我們需要預處理使得我們可以O(1)的查詢到1位置後面最近的'B'在哪裏,預處理見代碼
還需要把f[i][j]記錄清楚,因爲有很多重複的子問題我們沒有必要重複計算
代碼:
#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn = 2e5+55555;
const ll mod = 998244353;
const double eps = 1e-7;
char s[3456],t[3456];
int ne[3456][30],lens,lent;
int f[3456][3456];
void init() {
lens = strlen(s+1);
lent = strlen(t+1);
for(int i = 1;i<= 26;i++) {
int last = -1;
for(int j = lens;j>= 1;j--) {
if(s[j]-'A'+1 == i) {
last = j;
}
ne[j][i] = last;
}
}
return ;
}
int dfs(int x,int y) {
if(f[x][y]!= -1) return f[x][y]; //到達過這個狀態
if(y == lent+1) return 0; //把T串處理完了
if(x == lens+1) return inf; //把S串走完都沒處理完T串
int ans = inf;
int pos = ne[x][t[y]-'A'+1]; //T[y]在x之後的最近位置
if(pos!= -1) {
ans = min(ans,dfs(pos+1,y+1)); //不修改
} else {
ans = min(ans,dfs(x+1,y+1)+1); //修改
}
ans = min(ans,dfs(x+1,y+1)+1); //即使可以不修改也修改
return f[x][y] = ans;
}
int main() {
scanf(" %s %s",s+1,t+1);
init();
mem(f,-1);
int ans = dfs(1,1);
cout<<ans<<endl;
return 0;
}