BZOJ2121-字符串游戏
题解:
有难度的dp
感觉比较容易想到的dp是f[i][j]表示能否完全删除
转移时枚举i',j'转移,这样用hash可以做到l^4
但是 这是错的
因为我们可以将序列分为三段或者更多段相加
所以刚才的定义是有问题的
既然可以多段我们显然不能暴力枚举,那么我们就记录匹配了多少
f[i][j][k][l]表示i-j这段区间,能否匹配到k的l处
然后问题在于转移
第一种直接多匹配一位是很简单的
第二种在于要匹配一堆字符
我们能否直接枚举一个串来解决的
很明显示不能的,因为同刚才说的可能是多个搞在了一起形成的
所以我们还需要一个状态p[i][j]表示i-j能否被完全删除
时间复杂度l^3*n*m
数据不是很强加上有很多冗余状态于是就过了
网上代码有一些卡常数的东西。。
1.区间dp压掉一维,将区间dp变为从右向左枚举左端点,从左向右枚举右端点
2.最后一维状压,注意转移那里如果状压是可以做到(n+m)的
复杂度l^3*(n+m)
#include <bits/stdc++.h> using namespace std; #define rint register int #define IL inline #define rep(i,h,t) for (rint i=h;i<=t;i++) #define dep(i,t,h) for (rint i=t;i>=h;i--) char c[200],s[200][200]; int m; bool f[152][152][31][21],g[152][152]; int t[152]; int main() { freopen("1.in","r",stdin); freopen("1.out","w",stdout); ios::sync_with_stdio(false); cin>>c; cin>>m; rep(i,1,m) cin>>s[i]; int n=strlen(c); dep(i,n,1) c[i]=c[i-1]; rep(i,0,n) rep(j,0,n) rep(k,1,m) if (i>j) f[i][j][k][0]=1; rep(i,1,n) rep(j,1,n-i+1) { rint l=j,r=j+i-1; rep(k,1,m) { bool* a=f[l][r][k],*b=f[l][r-1][k]; int len=strlen(s[k]); rep(i1,1,len) if (c[r]==s[k][i1-1]&&b[i1-1]) a[i1]=1; rep(i1,1,len) rep(i2,l+1,r) if (g[i2][r]&&f[l][i2-1][k][i1]) a[i1]=1; if (a[len]) g[l][r]=1; } } rep(i,1,n) { rep(j,1,i) { t[i]=max(t[i],t[j-1]+((g[j][i]==1)?(i-j+1):0)); } t[i]=max(t[i-1],t[i]); } cout<<n-t[n]<<endl; return 0; }