https://www.luogu.org/problem/show?pid=1026#sub 点:同一字母不能重复用做首字母。 首先,处理出数组w[i][j] ,表示从i到j有多少个单词(当以同一个字母为首字母时,越短越好)。 然后,求出d[i],表示以i开头的最短单词的结尾的位置。 数组f[i][j]表示以i为终点,分为j份最多单词数。 然后状态转移方程: f[i][j]=max(f[i][j],f[k][j-1]+w[k+1][i]) (j-1<=k<=i-1)
代码:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int w[209][209],n,m,s; int a[209],d[209],f[209][209]; struct word{ int l; int c[201]; }b[10]; int comp(const word&x,const word&y) { return x.l<y.l?1:0; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=20*n;i++) { char c; cin>>c; a[i]=c-'a'+1; } scanf("%d",&s); for(int i=1;i<=s;i++) { char c[201]; cin>>c; for(int j=0;j<strlen(c);j++) b[i].c[j+1]=c[j]-'a'+1; b[i].l=strlen(c); } sort(b+1,b+s+1,comp); for(int i=1;i<=20*n;i++) { for(int j=1;j<=s;j++) { int p=0; for(int k=1;k<=b[j].l;k++) { int t=i+k-1; if(a[t]!=b[j].c[k]) p=1; } if(!p) {d[i]=i+b[j].l-1; break;} } } for(int i=1;i<=20*n;i++) { for(int j=i;j<=20*n;j++) { for(int p=i;p<=j;p++) if(d[p]<=j&&d[p]) w[i][j]++; } } for(int i=1;i<=20*n;i++) for(int j=1;j<=m;j++){ for(int k=j-1;k<=i-1;k++) f[i][j]=max(f[i][j],f[k][j-1]+w[k+1][i]); } printf("%d",f[20*n][m]); return 0; }