题目描述
对于两个字符串S和T,可以对任意次数执行以下操作:Select a string
S或T,在任何位置插入或删除字符。两个弦S和T之间的距离被定义了
作为使S和T相等的最小操作数。
您将得到两个字符串A[1…n]、B[1…m]和q查询。
在每个查询中,您将得到两个整数li和ri(1≤li≤ri≤n),您需要先查找距离
在连续的子串A[li…ri]和整个串B之间。
输入
输入的第一行包含单个整数T(1≤T≤10),即测试用例的数量。
对于每种情况,输入的第一行包含一个由n(1≤n≤100 000)小写组成的字符串a
英文字母。
输入的第二行包含一个字符串B,由m(1≤m≤20)个小写英文字母组成。
输入的第三行包含单个整数q(1≤q≤100000),表示查询的数量。
然后在下面的q行中,每行有两个整数li,ri(1≤li≤ri≤n),表示一个查询。
输出
对于每个查询,打印一行包含整数,表示答案
思路:求出两字符串的最长公共子串lcs;用r-l+1+m-2*lcs就可以求出操作数。
用dp[j]表示长度为j时,在i之前a串中最早结束的位置
用 nt[i][j]表示从i开始,首次出现字母j的位置 。找到最大公共子串长度。
代码
include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int inf=0x3f3f3f3f;
const int N=1e5+100;
char a[N],b[N];
int nt[N][26],dp[25];
int main(){
int w;
cin>>w;
while(w--)
{
scanf("%s%s",a+1,b+1);
int n=strlen(a+1),m=strlen(b+1);
for(int i=0;i<26;i++)
nt[n+1][i]=n+1;
for(int i=n;i>=0;i--)
{
for(int j=0;j<26;j++)
nt[i][j]=nt[i+1][j];
if(i>0)
nt[i][a[i]-'a']=i;
}
int q;
scanf("%d",&q);
while(q--)
{
int l,r;
scanf("%d%d",&l,&r);
int lcs=0;
memset(dp,inf,sizeof(dp));
dp[0]=l-1;
for(int i=1;i<=m;i++){
for(int j=m;j>=1;j--)
{
//dp[j]表示长度为j时,在i之前a串中最早结束的位置
// nt[i][j]表示从i开始,首次出现字母j的位置
if(dp[j-1]<=r&&nt[dp[j-1]+1][b[i]-'a']<=r)
dp[j]=min(dp[j],nt[dp[j-1]+1][b[i]-'a']);//最早出现b[i]的位置是长度加1后最早结束的位置
if(dp[j]<=r)
lcs=max(lcs,j);//更新最大公共子串
}
}
printf("%d\n",r-l+1+m-2*lcs);
}
}
}