Description
给定两个字符串 (S,T),每个询问会指定 (S) 的一个子串和 (T) 作为两个操作串 (A,B),每次可以选择任意一个字符串插入/删除一个字母,回答使得两个操作串相同的最小代价。(|S| le 10^5, |T| le 20)
Solution
插入操作是没用的,于是两个串的距离为 (|A|+|B|-2LCS(A,B))
预处理 (g[i][j]) 表示 (A[i..n]) 中字符 (j) 最早出现的下标
对于每个询问,暴力 DP
设 (f[i][j]) 表示与 (B[1..i]) 的公共序列长度达到 (j) 的 (A[l..r]) 的最短前缀的长度
[g[f[i][j]+1][B[i+1]] o f[i+1][j+1] \
f[i][j] o f[i+1][j]
]
即
[f[i][j]=min(f[i-1][j], g[f[i-1][j-1]][B[i]])
]
#include <bits/stdc++.h>
using namespace std;
const int N = 100005;
int n,m,q,s[N],t[N],f[25][25],g[N][30];
void make_g()
{
for(int c=0;c<26;c++)
{
int i=1,j=1;
while(i<=n)
{
while(s[i]!=c && i<=n) ++i;
if(s[i]==c) for(;j<=i;j++) g[j][c]=i;
++i;
}
while(j<=n) for(;j<=n;j++) g[j][c]=n+1;
}
for(int c=0;c<26;c++) g[0][c]=g[1][c], g[n+1][c]=g[n+2][c]=n+1;
}
void solve()
{
memset(f,0,sizeof f);
memset(g,0,sizeof g);
string ss,tt;
cin>>ss>>tt;
n=ss.length();
m=tt.length();
memset(s,0,sizeof s);
memset(t,0,sizeof t);
for(int i=1;i<=n;i++) s[i]=ss[i-1]-'a';
for(int i=1;i<=m;i++) t[i]=tt[i-1]-'a';
make_g();
cin>>q;
while(q--)
{
int l,r;
cin>>l>>r;
for(int i=0;i<=m;i++)
{
for(int j=0;j<=m;j++)
{
f[i][j]=n+1;
}
}
f[0][0]=l-1;
for(int i=1;i<=m;i++)
{
f[i][0]=f[i-1][0];
for(int j=1;j<=i;j++)
{
f[i][j]=min(f[i-1][j],g[f[i-1][j-1]+1][t[i]]);
}
}
int ans=0;
for(int i=1;i<=m;i++)
{
if(f[m][i]<=r) ans=i;
}
cout<<r-l+1+m-2*ans<<endl;
}
}
signed main()
{
ios::sync_with_stdio(false);
int t;
cin>>t;
while(t--)
{
solve();
}
}
/*
1
qaqaqwqaqaq
qaqwqaq
3
1 7
2 8
3 9
*/