区间DP
用C[l][r]表示l~r是否能被全部删掉,f[l][r][k][x]表示l~r和第k个串匹配是否能匹配到第x位,
注意到小串的长度≤21,而且f是个bool变量,所以可以把x压成int,
而推一下f的转移方程发现对于不同的l,他们之间是互不影响的,所以可以把l这一维删掉
那么枚举左端点,f[r][k]就代表了l~r,和第k个串的匹配情况,而且压位下转移十分方便,转移后要尝试跳一些可以完全删去的区间转移
当f[r][k],第length[k]位为1(即可以完全匹配)时C[l][r]=true
最后求出了所有C,再用一个DP求最多删去多少
code:
#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
inline void up(int &x,const int &y){if(x<y)x=y;}
const int maxn = 35;
const int maxm = 155;
const int maxl = 25;
char str[maxm],s[maxn][maxl];
int leng[maxn];
int len,n,f[maxm][maxn],mat[maxm][maxn];
bool C[maxm][maxm];
int ans[maxm];
int main()
{
scanf("%s",str+1); len=strlen(str+1);
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%s",s[i]+1),leng[i]=strlen(s[i]+1);
for(int i=1;i<=len;i++)
for(int j=1;j<=n;j++)
for(int k=1;k<=leng[j];k++) if(str[i]==s[j][k]) mat[i][j]|=(1<<k);
for(int l=len;l>=1;l--)
{
memset(f,0,sizeof f);
for(int k=1;k<=n;k++) f[l-1][k]=1;
for(int r=l;r<=len;r++)
{
for(int k=l;k<r;k++) if(C[l][k]&&C[k+1][r]) {C[l][r]=true; break;}
for(int k=1;k<=n;k++)
{
f[r][k]|=(f[r-1][k]<<1)&mat[r][k];
for(int x=r+1;x<=len;x++)
if(C[r+1][x]) f[x][k]|=f[r][k];
if(f[r][k]&(1<<leng[k])) C[l][r]=true;
}
}
}
memset(ans,0,sizeof ans);
for(int i=1;i<=len;i++)
{
ans[i]=ans[i-1];
for(int j=1;j<=i;j++)if(C[j][i]) up(ans[i],ans[j-1]+i-j+1);
}
printf("%d\n",len-ans[len]);
return 0;
}