1. 程式人生 > >Vijos1118 統計單詞個數

Vijos1118 統計單詞個數

  • 題目大意:給定一個長度20*p的字串以及一個字典,求將該字串分成k份後最多能包含多少個字典中的單詞(某個字母不能同時作為多個單詞的開頭)。

  • 思路:劃分型DP。先預處理出在各個區間中包含的單詞數,這裡可以只記錄最短單詞的長度,因為一個字母只能提供一個開頭。接下來就有f[i][k]=max(f[i][k],f[j][k-1]+cnt[j+1][i]),其中f[i][k]表示將前i個字元插入k個隔板(即劃分成k+1份,所以剛開始輸入的k要k–)所能獲得的最大單詞數。

  • 程式碼如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm> #include<string> using namespace std; const int maxn=205; const int maxk=45; int T; int p,kt,n,ss; string s; int len[maxn],f[maxn][maxk],cnt[maxn][maxn]; string si[10]; void init() { memset(len,0x3f,sizeof(len)); scanf("%d%d",&p,&kt); kt--; string
tmp; while (p--) { cin>>tmp; s+=tmp; } n=s.size(); scanf("%d",&ss); for (int i=1;i<=ss;++i) cin>>si[i]; } void sta() { for (int i=1;i<=ss;++i) { string tmp=si[i]; int now=0; while (true) { now=s.find(tmp,now); if
(now==-1) break; if (len[now]>tmp.size()) len[now]=tmp.size(); now++; } } for (int i=0;i<=n-1;++i) for (int j=i;j<=n-1;++j) for (int k=i;k<=j;++k) { if (len[k]==0x3f3f3f3f) continue; if (k+len[k]-1>j) continue; cnt[i][j]++; } memset(f,-1,sizeof(f)); for (int i=0;i<=n-1;++i) f[i][0]=cnt[0][i]; } void dp() { for (int i=0;i<=n-1;++i) for (int j=0;j<i;++j) for (int k=1;k<=kt;++k) { if (f[j][k-1]==-1) continue; /*這裡一定要加上特判,不能初始化為0,因為這是非法狀態,但相應的cnt可以合法,不加會導致非法狀態轉移進合法狀態,最終WA*/ if (f[j][k-1]+cnt[j+1][i]>f[i][k]) { f[i][k]=f[j][k-1]+cnt[j+1][i]; } } printf("%d\n",f[n-1][kt]); } int main() { init(); sta(); dp(); return 0; }