题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6774
题意:给你两个字符串 A,B, |A|≤105,|B|≤20|A|≤105,|B|≤20,每次询问 A 串的一个子串 A[L]...A[R],问该子串通过插入和删除一个字符的操作变得 和B字符串相等的最少操作数。
思路:设A子串为A,很容易可以发现最小操作数=|A|+|B|-2LCS(A,B)。因为插入操作其实是没有意义的,对于最小操作数来说。
这个问题就转换成了求子串和一个串的LCS。
如何求呢?
我们可以用一个nex数组,nex[i][j]表示在第i个字符后出现j字符的位置。这样就可以方便dp了。
定义 dp [ i ][ j ] , 其含义为 —— 和B的前 i 个字符 , 匹配了长度为 j 的 LCS 的最短 A 前缀。
那么有 dp[i][j]=min{dp[i][j],dp[i−1][j]}
dp[i][j]=min{dp[i][j],Next[dp[i−1][j−1]][B[i]]}
#include<bits/stdc++.h> using namespace std; typedef unsigned long long ull; int nex[100010][26],dp[21][26]; char str[100010],pat[30]; int main() { int t; scanf("%d",&t); while(t--) { int N,M; scanf("%s",str+1); scanf("%s",pat+1); N=strlen(str+1); M=strlen(pat+1); memset(nex,0x3f,sizeof(nex)); for(int i=N;i>=1;i--) { for(int j=0;j<26;j++) nex[i-1][j]=nex[i][j]; nex[i-1][str[i]-'a']=i; } int q; scanf("%d",&q); while(q--) { int L,R; scanf("%d%d",&L,&R); int ans=0; memset(dp,0x3f,sizeof(dp)); dp[0][0]=L-1; for(int i=1;i<=M;i++) { dp[i][0]=L-1; for(int j=1;j<=i;j++) { dp[i][j]=min(dp[i][j],dp[i-1][j]); if(dp[i-1][j-1]<R) dp[i][j]=min(dp[i][j],nex[dp[i-1][j-1]][pat[i]-'a']); } } int flag=0; for(int i=M;i>=0;i--) { for(int j=i;j<=M;j++) if(dp[j][i]<=R) { ans=i; flag=1; break; } if(flag==1) { break; } } //printf("ans:%d ",ans); printf("%d ",R-L+1+M-ans*2); } } return 0; }