【2020杭电多校round2】HDU6774 String Distance
题目大意
对于两个串\(s,t\)。你可以进行若干次操作。一次操作,你可以:
- 在\(s\)或\(t\)里的任意位置插入一个字符。
- 或在\(s\)或\(t\)里的任意位置删除一个字符。
我们定义两个串\(s,t\)的“距离”为,能使\(s,t\)相等的,最少操作次数。
现在给定两个串\(A[1\dots n],B[1\dots m]\),和\(q\)次询问。
每次询问给定两个整数\(l_i,r_i\) (\(1\leq l_i\leq r_i\leq n\)),求\(A[l_i\dots r_i]\)和整个\(B\)的“距离”。
\(T\)组数据。
数据范围:\(1\leq T\leq 10\),\(1\leq n,q\leq 10^5\),\(1\leq m\leq 20\)。
本题题解
首先,经过初步分析,两个串\(s,t\)的距离就等于\(|s|+|t|-2\cdot \text{lcs}(s,t)\)。其中\(\text{lcs}\)表示最长公共序列。于是问题转化为,每次求\(A[l\dots r]\)和\(B\)的最长公共子序列。
注意到\(B\)很小而\(A\)很大,所以要努力使一次询问的复杂度,与\(A\)的长度(也就是和\(r-l+1\))无关。
可以做一些预处理。设\(dp[i][j][k]\),表示\(A[i\dots n]\)和\(B[j\dots m]\)这两个串,所有长度为\(k\)的公共子序列里,在\(A\)上结尾最小是多少。换句话说,\(dp[i][j][k]\)就是最小的\(p\geq i\),满足\(A[i\dots p]\)和\(B[j\dots m]\)有长度为\(k\)的公共子序列。如果不存在这样的\(p\),则令\(dp[i][j][k]=n+1\)。
这个预处理挺有技巧的,不是简单枚举,而需要DP:用已知的来推未知的。我们倒着枚举所有\(i\)。每个\(dp[i][j][k]\),都从\(dp[i+1][\dots][\dots]\)转移过来。转移时,有两种情况:\(i\)在不在这个公共子序列里,分别考虑一下,两种情况取个\(\min\)即可。
预处理完\(dp\)数组后。考虑回答询问。对一次询问,\(A\)的开始位置是\(l\)我们已经知道了。可以暴力枚举\(B\)的开始位置\(j\),再暴力枚举公共子序列长度\(k\),看\(dp[l][j][k]\)是否小于等于\(r\)。如果小于等于\(r\),用\(k\)更新\(\text{lcs}\)的长度即可。
时间复杂度\(O((n+q)m^2)\)。
参考代码(ps. 本题需要使用fread
,可以去这里粘个板子):
#include <bits/stdc++.h>
using namespace std;
#define pb push_back
#define mk make_pair
#define lob lower_bound
#define upb upper_bound
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
template<typename T>inline void ckmax(T& x,T y){x=(y>x?y:x);}
template<typename T>inline void ckmin(T& x,T y){x=(y<x?y:x);}
const int MAXN=1e5,MAXM=20;
const int INF=1e9;
int n,m,nxt_b[MAXN+5][26];
int f[MAXN+5][MAXM+5][MAXM+5];
char a[MAXN+5],b[MAXM+5];
void get_nxt(char* s,int len,int nxt[MAXN+5][26]){
static int pos[26];
for(int i=0;i<26;++i)
pos[i]=len+1;
for(int i=len+1;i>=0;--i){
for(int j=0;j<26;++j)
nxt[i][j]=pos[j];
if(i!=0&&i!=len+1)
pos[s[i]-'a']=i;
}
}
void solve_case(){
cin>>(a+1); n=strlen(a+1);
cin>>(b+1); m=strlen(b+1);
get_nxt(b,m,nxt_b);
for(int j=1;j<=m;++j){
for(int k=1;k<=m;++k){
f[n+1][j][k]=n+1;
}
}
for(int i=n;i>=1;--i){
for(int j=1;j<=m;++j){
f[i][j][1]=f[i+1][j][1];
if(nxt_b[j-1][a[i]-'a']<=m){
f[i][j][1]=i;
}
for(int k=2;k<=m;++k){
f[i][j][k]=f[i+1][j][k];
if(nxt_b[j-1][a[i]-'a']<m)
ckmin(f[i][j][k],f[i+1][nxt_b[j-1][a[i]-'a']+1][k-1]);
}
}
}
// while(1){
// int i,j,k;
// cin>>i>>j>>k;
// cout<<f[i][j][k]<<endl;
// }
int q;cin>>q;while(q--){
int l,r;cin>>l>>r;
int ans=0;
for(int j=1;j<=m;++j){
for(int k=1;k<=m-j+1;++k){
if(f[l][j][k] > r)
break;
ckmax(ans,k);
}
}
ans=r-l+1+m-2*ans;
cout<<ans<<endl;
}
}
int main() {
int T;cin>>T;while(T--){
solve_case();
}
return 0;
}