2020HDU多校2 String Distance

题目链接

问题重述

给你一个长度在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[i1][j1]+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[i1][j1]<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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值