$dp$。
设$dp[i][j]$为到$i$位置,切成了$j$段的最大收益,然后枚举一下$f$,$dp[i][j]=max(dp[f][j-1]+v[f+1][i])$。一段区间的价值可以用区间$dp$求得。
#include<cstdio> #include<cstring> #include<cmath> #include<vector> #include<map> #include<set> #include<queue> #include<stack> #include<algorithm> #include<iostream> using namespace std; char s[300]; int p,k,len; char h[10][30]; int x; int dp[205][60], v[205][205]; bool check(int L,int R,int g) { int p1=L,p2=0,Leng = strlen(h[g]); if(Leng>R-L+1) return 0; while(1) { if(p2==Leng) break; if(s[p1]!=h[g][p2]) return 0; p1++; p2++; } return 1; } int main() { while(~scanf("%d%d",&p,&k)) { memset(s,0,sizeof s); for(int i=1;i<=p;i++) { char t[30]; scanf("%s",t); strcat(s,t); } len=strlen(s); scanf("%d",&x); for(int i=1;i<=x;i++) scanf("%s",h[i]); memset(dp,0,sizeof dp); memset(v,0,sizeof v); for(int Len=1;Len<=len;Len++) { for(int st=0;st<len;st++) { int en = st+Len-1; if(en>=len) continue; for(int p=st;p<en;p++) v[st][en] = max(v[st][en],v[st][p]+v[p+1][en]); for(int i=1;i<=x;i++) if(check(st,en,i)) v[st][en] = max(v[st][en],v[st+1][en]+1); } } for(int i=0;i<len;i++) dp[i][1] = v[0][i]; for(int j=2;j<=k;j++) { for(int i=j-1;i<len;i++) { for(int f=j-2;f<i;f++) { dp[i][j]=max(dp[i][j],dp[f][j-1]+v[f+1][i]); } } } printf("%d\n",dp[len-1][k]); } return 0; }