问题重述
给你一个长度在1e5之内的小写字符串A,和一个长度在20之内的小写字符串B。q(1e5)次询问,每次询问A[l…r]与B的编辑距离.
核心问题
如何求这个LCS? 才能做到稳定 l e n B 2 lenB^2 lenB2的复杂度?
解决方案: 利用LCS向LIS的转化, 魔改一下, 然后求解.
LCS转LIS
利用F[i][j]表示A[i…lenA], 中j+'a’字符出现的最靠前的位置. 可以通过O(NC)求出来.
然后利用 DP[i][j] = pos, 维护 B[1…i]中, 长度为j的LCS的最靠前的A[l…pos].
这个的正确性可以理解为: 在匹配过LCS之后, B中对应LCS的字符在A中出现的位置是递增的. 则对于相同长度j, 我们期望其最后一个位置更小.
转移方程为:
F
[
D
P
[
i
−
1
]
[
j
−
1
]
+
1
]
[
B
[
i
]
−
′
a
′
]
→
D
P
[
i
]
[
j
]
F[DP[i-1][j-1]+1][B[i]-'a'] \rightarrow DP[i][j]
F[DP[i−1][j−1]+1][B[i]−′a′]→DP[i][j]
这样就会省下 去找对应字符满足位置关系的位置的复杂度
边界问题: 不存在的字符 F[i][j] = + ∞ \infty ∞, 除了DP[0][0]=l-1(后面的那个转移方程+1需要用)之外, DP为INF. 这样需要注意每次转移的时候判断一下 D P [ i − 1 ] [ j − 1 ] < r DP[i-1][j-1]<r DP[i−1][j−1]<r
代码
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+17, C = 27, M = 27;
char A[N], B[M];
int F[N][C], lenA, lenB, q, l, r, DP[M][M];
int work2(){
memset(DP, 0x3f, sizeof(DP));
DP[0][0] = l-1;
for(int i=0;i<lenB;++i){
for(int j=0;j<=i;++j){
DP[i+1][j] = min(DP[i+1][j], DP[i][j]);
if(DP[i][j] < r)
DP[i+1][j+1] = min(DP[i+1][j+1], F[DP[i][j]+1][B[i+1]-'a']);
}
}
for(int i=lenB;i;--i)
for(int j=lenB;j;--j)
if(DP[j][i] <= r)
return i;
return 0;
}
void work(){
scanf("%s", A+1);
scanf("%s", B+1);
lenA = strlen(A+1);
lenB = strlen(B+1);
for(int j=0;j<C;++j){
F[lenA+1][j] = lenA+1;
}
for(int i=lenA;i;--i){
for(int j=0;j<C;++j)
F[i][j] = F[i+1][j];
F[i][A[i]-'a'] = i;
}
scanf("%d",&q);
for(int i=1;i<=q;++i){
scanf("%d %d",&l,&r);
printf("%d\n", r-l+1+lenB-2*work2());
}
return;
}
int main(){
int T;scanf("%d",&T);
while(T--) work();
return 0;
}