[JOIG 2024] 名前 / Name 题解
套路地考虑动态规划:如果令
考虑增加状态:有一种比较朴素的方法是记录答案的最后
接着观察到上面很多状态都是冗余的,因为我们只需要关心 unordered_map 存储状态并且使用 BFS 转移——因为大多数情况下只会使用 * 或者某个其他字符来代替,这样能大大压缩状态数量,但是常数会上天。我在 VP 的时候实现了这种做法,可以获得
继续观察性质,考虑是否存在一些比较简洁且便于转移的状态。由于只需要记最后 * 的情况),时间复杂度后面乘上的系数就是 *,即可完成转移。
时间复杂度
放代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline void chmin(int &x,int y){if(y<x)x=y;}
const int I=1e18;
main(){
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int n,m,k; string s,t; cin>>n>>m>>k>>s>>t;
vector f(n+1,vector(m+1,vector(4,vector(4,vector<int>(4,I)))));
queue<tuple<int,int,int,int,int> > q;
f[0][0][0][0][0]=0,q.emplace(0,0,0,0,0);
auto upd=[&](int u,int v,int x,int y,int z,int w){
if(f[u][v][x][y][z]==I)q.emplace(u,v,x,y,z);
chmin(f[u][v][x][y][z],w);
}; // 状态转移
while(!q.empty()){
auto [u,v,x,y,z]=q.front(); q.pop();
if(u==n&&v==m)cout<<f[u][v][x][y][z]<<endl,exit(0);
vector<int> c={x,y,z};
if(u<n&&v<m&&s[u]==t[v]){
bool w=true;
for(int i=0,a=u-1,b=v-1;i<k;i++){
if(c[i]&1&&s[u]==s[a--])w=false;
if(c[i]>>1&1&&s[u]==t[b--])w=false;
} // 还原出最后 k 个字符,判断是否能进行转移
if(w)upd(u+1,v+1,3,x,y,f[u][v][x][y][z]+1);
}
else{
if(u<n){
bool w=true;
for(int i=0,a=u-1,b=v-1;i<k;i++){
if(c[i]&1&&s[u]==s[a--])w=false;
if(c[i]>>1&1&&s[u]==t[b--])w=false;
}
if(w)upd(u+1,v,1,x,y,f[u][v][x][y][z]+1);
}
if(v<m){
bool w=true;
for(int i=0,a=u-1,b=v-1;i<k;i++){
if(c[i]&1&&t[v]==s[a--])w=false;
if(c[i]>>1&1&&t[v]==t[b--])w=false;
}
if(w)upd(u,v+1,2,x,y,f[u][v][x][y][z]+1);
}
}
upd(u,v,0,x,y,f[u][v][x][y][z]+1); // 加入一个 *
}
return 0;
}