题意:
有两个字符串,问第一个字符串最少要删除多少个字符才可以使得第二个字符串不是第一个字符串的子串。第一个字符串长度小于等于10000,第二个小于等于1000。
题解:
一看这个数据范围,很明显就是O(nm)的效率dp,那么很显然f[i][j]表示第一个字符串的前i位,恰好匹配到了第二个字符串的第j位所删除的最少的字符数量。
转移的时候如果下一位删除,那么f[i][j]直接转移f[i+1][j]即可。
如果下一位不删除而是保留,那么我们需要知道加上了第一个字符串的i+1位之后,能匹配到第二个字符串第几位。
如果a[i+1]=b[j+1]那么很显然f[i][j]转移到f[i+1][j+1]
如果不相等,那么接下来就是j不断跳kmp中的next数组,去尽量匹配a[i+1]。
当然,每次暴力跳效率显然是错误的。那么我们进行一下记忆化,就可以做到O(1)转移了。
#include<cstdio> #include<algorithm> #include<cstring> #include<cstdlib> using namespace std; const int INF=1e9; int n,m,nex[10002],f[10002][1002],ans=INF,t[30][10002]; char a[10002],b[10002]; int main() { scanf("%s%s",a+1,b+1);n=strlen(a+1);m=strlen(b+1); for (int i=2;i<=m;i++) { int x=i-1; while(x && b[nex[x]+1]!=b[i])x=nex[x]; if (b[nex[x]+1]==b[i])nex[i]=nex[x]+1; } for (int i=0;i<=n;i++) for (int j=0;j<=m;j++) f[i][j]=INF; f[0][0]=0;memset(t,-1,sizeof(t)); for (int i=0;i<n;i++) for (int j=0;j<m;j++) { if (t[a[i+1]-'a'][j]==-1) { if (!j)t[a[i+1]-'a'][j]=0; else if (b[j+1]==a[i+1])t[a[i+1]-'a'][j]=j; else t[a[i+1]-'a'][j]=t[a[i+1]-'a'][nex[j]]; } if (a[i+1]==b[t[a[i+1]-'a'][j]+1])f[i+1][t[a[i+1]-'a'][j]+1]=min(f[i+1][t[a[i+1]-'a'][j]+1],f[i][j]); else f[i+1][0]=min(f[i+1][0],f[i][j]); f[i+1][j]=min(f[i+1][j],f[i][j]+1); } for (int i=0;i<m;i++)ans=min(ans,f[n][i]); printf("%d ",ans); return 0; }