题意:
给定长度为n的串s,长度为m的串t,
一次操作,你可以删除s串或者t串一个字符或者在任意位置插入一个字符
q次询问,每次询问给出L,R,问s[L,R]和t至少操作几次才能变得一样
数据范围:n<=1e5,m<=20,q<=1e5
解法:
s串数据范围比较大,询问也比较多,但是观察到m比较小,从m入手
发现插入和删除操作可以在s串也可以在t串上进行,那么其实只进行删除就行了
计算最小删除次数可以转化为计算最大暴力次数,最大保留次数就是两个串的最大公共子序列长度
设最大公共子序列长度为ma,那么答案就是r-l+1-ma*2
令d(i,j)表示t串前i位在s串上匹配j位时的最小下标,
d(i,j)可以从d(i,j-1)和d(i-1,j-1)转移而来
从d(i-1,j-1)转移需要找s串d(i-1,j-1)位置后面第一个s(i),可以用序列自动机进行快速计算
code:
#include<bits/stdc++.h>
using namespace std;
const int maxm=1e5+5;
char s[maxm],t[25];
int nt[maxm][26];
int d[25][25];
int n,m;
signed main(){
int T;scanf("%d",&T);
while(T--){
scanf("%s%s",s+1,t+1);
n=strlen(s+1),m=strlen(t+1);
//预处理部分
for(int j=0;j<26;j++){
nt[n][j]=0;
}
for(int i=n;i>=1;i--){
for(int j=0;j<26;j++){
nt[i-1][j]=nt[i][j];
}
nt[i-1][s[i]-'a']=i;
}
//
int q;scanf("%d",&q);
while(q--){
int l,r;scanf("%d%d",&l,&r);
for(int i=0;i<=m;i++){
for(int j=0;j<=m;j++){
d[i][j]=n+1;
}
}
int ma=0;
d[0][0]=l-1;
for(int i=1;i<=m;i++){//d[i][j]表示t串前i个匹配j个的最小位置
d[i][0]=l-1;
for(int j=1;j<=i;j++){
if(j<=i-1)d[i][j]=min(d[i][j],d[i-1][j]);//从d[i-1][j]转移
if(nt[d[i-1][j-1]][t[i]-'a']&&nt[d[i-1][j-1]][t[i]-'a']<=r){//从d[i-1][j-1]转移
d[i][j]=min(d[i][j],nt[d[i-1][j-1]][t[i]-'a']);
}
if(d[i][j]<=r)ma=max(ma,j);
}
}
int ans=r-l+1+m-2*ma;
printf("%d\n",ans);
}
}
return 0;
}