【UVA1371】Period (二分+DP)
题意:
给出两个字符串A,B将B分解成若干个子字符串,然后每个子字符串都要经过编辑变成字符串A,所有子串中编辑最多的次数即为当前状态下的最大编辑次数,要求求最小的最大编辑次数。
编辑操作包括修改、删除和插入。(|A|<=5000,|B|<=50)
分析:
因为A的长度较大,直接算出A每个区间对应B的编辑次数的方法是不可取的。因为是求最大值最小化,可以想到二分答案然后判断。
判断的时候用DP,dp[i][j]表示到1~i的字符串匹配到j的最大编辑次数。当dp[i][m]<=mid时(mid为二分出来的当前答案),就可以将dp[i][0] 置0,表示做为起点(因为此时可以在i后面分段)。
打代码的时候出现了一个问题,当我dp[i][m]<=mid时,我把dp[i][0] 置0的同时,还把dp[i]的其他值全部置成INF,这样做是有问题的。比如,x=abcdabcabb,y=abc,当mid=1时,前面两个字符ab的编辑长度刚好为一,此时我直接把ab分成一段了,这样导致我把第三个字符c扔在一边。其实把abc一起放成一段更好。问题就出在我把dp[i][0] 置0的同时还把dp[i]的其他值全部置成INF。这样做相当于默认我把i前面的分成一段,但事实上或许再加一个字符编辑距离仍然不超过mid,这样可能会使后面的答案更优而我却没有找到最优解,于是就产生判断错误。所以只需把dp[i][0] 置0即可,这样子就表示我们可以把i前面的字符分成一段,当然也可以再加一些字符再分成一段。
代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstdlib> 4 #include<cstring> 5 #include<iostream> 6 using namespace std; 7 #define Maxn 5010 8 #define Maxm 60 9 10 char a[Maxn],b[Maxm]; 11 int n,m; 12 13 int f[Maxn][Maxm]; 14 15 int mymin(int x,int y) {return x<y?x:y;} 16 17 bool dp(int x) 18 { 19 memset(f,63,sizeof(f)); 20 for(int i=0;i<=m;i++) f[0][i]=i; 21 for(int i=1;i<=n;i++) 22 { 23 for(int j=1;j<=m;j++) 24 { 25 if(a[i]==b[j]) f[i][j]=mymin(f[i][j],f[i-1][j-1]); 26 else f[i][j]=mymin(f[i][j],f[i-1][j-1]+1); 27 f[i][j]=mymin(f[i][j],f[i-1][j]+1); 28 f[i][j]=mymin(f[i][j],f[i][j-1]+1); 29 } 30 if(f[i][m]<=x) 31 { 32 if(i==n) return 1; 33 f[i][0]=0; 34 } 35 if(i==n) return 0; 36 } 37 } 38 39 void ffind() 40 { 41 scanf("%s%s",b+1,a+1); 42 n=strlen(a+1);m=strlen(b+1); 43 int l=0,r=n; 44 while(l<r) 45 { 46 int mid=(l+r)>>1; 47 if(dp(mid)) r=mid; 48 else l=mid+1; 49 } 50 printf("%d\n",l); 51 } 52 53 int main() 54 { 55 int T; 56 scanf("%d",&T); 57 while(T--) 58 { 59 ffind(); 60 } 61 return 0; 62 }
2016-03-06 16:47:51
貌似已经看到很多题二分加DP了~