2020 Multi-University Training Contest 2(杭电多校第二场)String Distance
原题链接:http://acm.hdu.edu.cn/showproblem.php?pid=6774
先翻译一下题意:
给定字符串A、B,求下标从li到ri的A的子串与B的距离。
两个字符串的距离定义为在两个串中任意添加或删去字符使得两个串相等的最少操作数。
略微观察题意之后不难发现,对于两个串中不同的字符,将两个字符都删去或者将不同的替换所花费的步数都是两步;对于长度不同的两个串,删去或者添加都是一步。
因此这道题只需要求出最长公共子序列的长度,然后就能求得res=|A|+|B|-2*|LCS|。
但是这里n、q都有1e5,显然用常规的LCS来做会直接T。而B的长度m却非常小,所以可以用一种巧妙的方法来求出LCS的长度。
首先预处理出ed数组表示字符在A的后缀中最早出现的位置,然后通过DP来求出答案。
DP[i][j]表示一个最短的A(l,dp[i][j])与B中前i个字符的LCS为j。
转移方程为DP[i][j]=min(dp[i][j],ed[B[i]-'a'][dp[i-1][j-1]+1]);
这样的时间复杂度就只有O(qm^2)。
下面是本人的代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 using namespace std; 5 const int INF=0X3f3f3f3f; 6 char s1[100005],s2[25]; 7 int ed[27][100005],dp[25][25]; 8 int q; 9 int main(){ 10 /* freopen("1012.in","r",stdin); 11 freopen("1012.out","w",stdout);*/ 12 int T; 13 scanf("%d",&T); 14 while (T--){ 15 scanf("%s",s1+1); 16 scanf("%s",s2+1); 17 int l1=strlen(s1+1),l2=strlen(s2+1); 18 for (int i=0; i<26; i++) ed[i][l1+1]=l1+1; 19 for (int i=l1; i>=1; i--){ 20 for (int j=0; j<26; j++){ 21 ed[j][i]=ed[j][i+1]; 22 } 23 ed[s1[i]-'a'][i]=i; 24 }//处理每个字符在A的后缀中出现的最早位置 25 26 scanf("%d",&q); 27 while (q--){ 28 int l,r; 29 scanf("%d%d",&l,&r); 30 for (int i=0; i<=l2; i++) 31 for (int j=0; j<=l2; j++) 32 dp[i][j]=INF; 33 //注意初始化要包括到0,否则边界出问题会WA 34 dp[0][0]=l-1; 35 for (int i=1; i<=l2; i++){ 36 for (int j=0; j<=i; j++){ 37 if (j==0) { 38 dp[i][j]=dp[i-1][j]; 39 continue; 40 } 41 if (dp[i][j]>dp[i-1][j]) dp[i][j]=dp[i-1][j]; 42 if (dp[i-1][j-1]<r){ 43 dp[i][j]=min(dp[i][j],ed[s2[i]-'a'][dp[i-1][j-1]+1]); 44 } 45 } 46 } 47 int res=0,flag=1; 48 for(int i=l2; i>=1; i--){ 49 for(int j=i; j<=l2; j++){ 50 if(dp[j][i]<=r && dp[j][i]!=0){ //根据dp[i][j]的含义,求出LCS的长度 51 res=i; 52 flag=0; 53 break; 54 } 55 } 56 if (flag==0) break; 57 } 58 printf("%d\n",r-l+1+l2-2*res); 59 } 60 } 61 return 0; 62 }