/**//* 题意:给出A,B串,问最小代价将B变为A的子串,代价不能超过LIM A<=10^6, B<=1000,LIM<=30
经典编辑距离方程为 dp[i,j] = min( dp[i-1,j-1]+(A[i]!=B[j]) , dp[i-1,j]+1 , dp[i,j-1]+1) dp[0,0]=0 O(nm) 现在允许B是A的子串,只需初始化时 dp[i,0] = 0 即可 但还是O(nm)
由于要求代价<=LIM,所以打算对于会超过这个代价的dp[i,j]不去计算 这样子平均复杂度为O(n*LIM)
用一个last记录上一行最后需要计算的合法位置 由于dp[i,j]相邻元素相差不超过1,最好的情况是dp[i,last+1] = LIM,这时dp[i-1,last] = LIM 而dp[i,last+2],dp[i,last+3]等肯定会大于LIM!! 所以对于当前行,我们只需计算到last+1即可!!
由此得到算法:(有点像归纳) 初始时last = LIM 因为dp[0,LIM] = LIM 对于当前行,只需计算到last+1处即可 若dp[now,last]>LIM 再调整一下
启示:平常要计算整个n*m的表 现在只计算每一行的前面一些元素,因为后面的那些代价会超过LIM! */ #include<cstdio> #include<cstring> #include<algorithm>
using namespace std;
inline int min(int a,int b){return a<b?a:b;}
const int MAXN = 1000010;
char A[MAXN],B[1010]; int dp[2][1010];
int main() { //freopen("in","r",stdin); int lim; while(~scanf("%s%s",A+1,B+1)) { scanf("%d",&lim); //dp[i,j] = min{ dp[i-1,j]+1 , dp[i,j-1]+1 , dp[i-1,j-1]+(A[i]!=B[j]) } //dp[i,0] = 0 //ans = min{ dp[i,len] } for(int j=0;j<=lim;j++)dp[0][j] = j; int len = strlen(B+1); int ans = lim+1,last = min(lim,len);//last记录上一次最后计算的位置 当前最多计算的位置为last+1 int pre = 0,now = 1; for(int i=1,j;A[i];i++) { dp[now][0] = 0; for(j=1;j<=last;j++) { dp[now][j] = min(dp[pre][j],dp[now][j-1])+1; dp[now][j] = min(dp[now][j],dp[pre][j-1]+(A[i]!=B[j])); } //j = last+1 dp[now][j] = min(dp[now][j-1]+1,dp[pre][j-1]+(A[i]!=B[j])); if(j>=len)ans = min(ans,dp[now][len]); last = min(len,last+1); while(dp[now][last]>lim)last--; swap(pre,now); } printf("%d\n",ans>lim?-1:ans); } return 0; }
|