BZOJ2121 字符串游戏
想出来了一半,然后看了眼题解,果然还是和状压不熟导致的。
题目大意
给你一个字符串 L L L 和一个有 n n n 个字符串的集合 S S S,每次操作可以在 L L L 中选择一个子串,如果这个子串在集合 S S S 中,那么这个子串就会被删去,而 L L L 的剩余部分会拼接在一起。求最后 L L L 的长度最小可能是多少。
∣ L ∣ ≤ 150 |L|≤150 ∣L∣≤150, n ≤ 30 n≤30 n≤30, ∣ S i ∣ ≤ 20 |Si|≤20 ∣Si∣≤20。
题解
从左到右把字符一个一个加进来。 f i , j , k , l f_{i,j,k,l} fi,j,k,l 表示从第 i i i 个字符到第 j j j 个字符形成的子串能否匹配到 S k S_k Sk 的第 l l l 位。然后再把最后一维压缩,加点预处理就过了。复杂度 O ( n ∗ ∣ L ∣ 3 ) O(n*|L|^3) O(n∗∣L∣3)。
把最后一维压掉我居然没想到。我是状压领域脑瘫。
Code
#include<bits/stdc++.h>
using namespace std;
const int N=150+5,M=30+5,K=20+5;
int n,m,len[N],ans[N],f[N][N][M],g[N][M];
bool ck[N][N];
char s[M][K],s1[N];
int main(){
scanf("%s%d",s1+1,&m);
n=strlen(s1+1);
for(int i=1;i<=m;i++){
scanf("%s",s[i]+1);
len[i]=strlen(s[i]+1);
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
for(int k=1;k<=len[j];k++)
if(s1[i]==s[j][k])
g[i][j]|=(1<<k-1);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++)
if(s1[i]==s[j][1]){
f[i][i][j]=1;
if(len[j]==1) ck[i][i]=true;
}
for(int j=1;j<i;j++)
for(int k=1;k<=j;k++)
if(ck[i-1][j+1]||j+1>i-1)
for(int o=1;o<=m;o++){
f[i][k][o]|=((f[j][k][o]<<1)&g[i][o]&((1<<len[o])-1));
if((f[i][k][o]>>len[o]-1)&1) ck[i][k]=true;
}
for(int j=i;j>=1;j--)
for(int k=j-1;k>=1;k--)
if(ck[i][j]&&ck[j-1][k]) ck[i][k]=true;
}
for(int i=1;i<=n;i++){
ans[i]=ans[i-1]+1;
for(int j=1;j<=i;j++)
if(ck[i][j]) ans[i]=min(ans[i],ans[j-1]);
}
printf("%d",ans[n]);
return 0;
}