P1026 統計單詞個數
P1026 統計單詞個數
題目描述
給出一個長度不超過200的由小寫英文字母組成的字母串(約定;該字串以每行20個字母的方式輸入,且保證每行一定為20個)。要求將此字母串分成k份(1<k<=40),且每份中包含的單詞個數加起來總數最大(每份中包含的單詞可以部分重疊。當選用一個單詞之後,其第一個字母不能再用。例如字符串this中可包含this和is,選用this之後就不能包含th)。
單詞在給出的一個不超過6個單詞的字典中。
要求輸出最大的個數。
輸入輸出格式
輸入格式:
每組的第一行有二個正整數(p,k)
p表示字串的行數;
k表示分為k個部分。
接下來的p行,每行均有20個字符。
再接下來有一個正整數s,表示字典中單詞個數。(1<=s<=6)
接下來的s行,每行均有一個單詞。
輸出格式:
一個整數,分別對應每組測試數據的相應結果。
輸入輸出樣例
輸入樣例#1:1 3 thisisabookyouareaoh 4 is a ok sab輸出樣例#1:
7
說明
this/isabookyoua/reaoh
【問題分析】
剛看到這個題目覺得很迷茫,沒入手點但是突然看到了閃亮的突破口:題目中說this包含this和is 但不包含th這也就是說在一個串內對於一個固定了起點的單詞只能用一次,即使他還可以構成別的單詞但他還是用一次。比如:串:thisa
字典:this is th
串中有this is th這三個單詞,但是對於this 和 th 只用一次,也就是說枚舉一下構成單詞的起點,只要以該起點的串中包含可以構成一個以該起點開頭的單詞,那麽就說明這個串中多包含一個單詞。
這樣可以得出下面的結果:
枚舉的起點 結論:
t 至少包含1個
h 至少包含1個
i 至少包含2個
s 至少包含2個
a 至少包含2個
考慮到這裏,就有點眉目了。
題目中要將串分k個部分也就是說從一個點截斷後一個單詞就未必可以構成了。比如上例要分3個部分,合理的其中的一個部分至多有3個字母,這樣this這個單詞就構不成了。
要是分5個部分,那就連一個單詞都構不成了。
這樣就需要對上面做個改動,上面的只控制了起點,而在題目中還需要限制終點,分完幾個部分後,每部分終點不同可以構成的單詞就不同了。
這樣就需要再枚舉終點了。
設計一個二維數組sum[i,j]統計從i到j的串中包含的單詞的個數
狀態轉移方程:
sum[i+1,j]+1 (s[i,j]中包含以s[i]開頭的單詞)
sum[i,j]= sum[i+1,j] (與上面相反)
註:(1)這裏枚舉字符的起點的順序是從尾到頭的。
(2)有人把上面這次也看做是一次動態規劃,但我覺得更準確的說是遞推。
求出所有的sum還差一步,就是不同的劃分方法顯然結果是不一樣的,但是對於求解的問題我們可以這樣把原問題分解成子問題:求把一個串分成k部分的最多單詞個數可以看做是先把串的最後一部分分出來,再把前面一部分分解成k-1個部分,顯然決策就是找到一種劃分的方法是前面的k-1部分的單詞+最後一部分的單詞最多。
顯然這個問題滿足最優化原理,那滿不滿足無後效性呢?
對於一個串分解出最後一部分在分解前面的那部分是根本就不會涉及分好的這部分,換句話說每次分解都會把串分解的更小,對於分解這個更小的串不會用到不屬於這個小串的元素。這就滿足無後效性。
具體求解過程:
設計一個狀態opt[i,j]表示把從1到j的串分成i份可以得到最多的單詞的個數。決策就是枚舉分割點使當前這種分割方法可以獲得最多的單詞。
狀態轉移方程:opt[i,j]=max(opt[i-1,t]+sum[t+1,j]) (i<t<j)
邊界條件:opt[1,i]=sum[1,i] (0<i<=L)
時間復雜度:狀態數O(N2)*決策數O(N)=O(N3),空間復雜度:O(N2)。
sum[i][j]表示第i個字母到第j個字母一共可以形成多少個單詞。
sum[i][j]=sum[i][-1]+(包含可以添加最後一個字母j的單詞的總個數)。
註意到題目中有一個非常特殊的地方,就是以串中某個位置的字母為首字母,最多只能分出一個單詞。由於在拆分字符串的過程中,如果以某位置為首某個較短單詞被截斷,那麽以該位置為首的較長單詞必然也會被截斷。也就是說,對於各個位置來說我們選取較短的單詞總不會比選取較長的單詞所形成的單詞少。這樣我們可以定義一個在位置的參數表示以位置的字母為首字母,所能形成的最短單詞的長度。這樣如果在這個位置加上這個單詞的長度之內截斷,則以該位置為首字母就不能形成單詞,否則就可能形成一個單詞。這樣對於所有的不同個首位置,我們只要在各個位置依次對各個單詞進行匹配就以得出所對應的的值,這一部分的復雜度為O(wl2)。然後是計算把字串分為多個部分的過程中有什麽單詞可以留下。
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 const int maxn=250; 5 const int maxm=10; 6 7 string s; 8 int slen; 9 string words[maxm]; 10 int wordslen[maxm]; 11 int f[maxn][maxm]; 12 int sum[maxn][maxn]; 13 int n,k,m; 14 15 //讀入數據 16 void init() 17 { 18 string tmp; 19 cin>>n>>k; 20 for (int i=1;i<=n;i++) 21 { 22 cin>>tmp; 23 s+=tmp; 24 } 25 slen=s.size(); 26 //讀入字典 27 cin>>m; 28 for (int i=1;i<=m;i++) 29 { 30 cin>>words[i]; 31 wordslen[i]=words[i].size(); 32 } 33 } 34 35 //l頭r尾 36 int add(int l,int r) 37 { 38 int ans=0; 39 //前面還有字符 40 if (r-1>=0) ans=sum[l][r-1]; 41 bool vis[maxn]={0}; 42 for (int i=1;i<=m;i++) 43 { 44 int qd=r-wordslen[i]+1; 45 if (qd<l) continue; 46 if (qd==s.find(words[i],qd)) 47 { 48 if (vis[qd]) continue; 49 vis[qd]=1; 50 ans++; 51 for (int j=1;j<=m;j++) 52 { 53 int dq=r-wordslen[j]; 54 if (dq==qd) 55 if (dq==s.find(words[j],dq)) 56 { 57 ans--; 58 break; 59 } 60 } 61 } 62 } 63 return ans; 64 } 65 66 //得到sum數組 67 void gsum() 68 { 69 for (int i=0;i<=slen-1;i++)//串頭 70 for (int j=i;j<=slen-1;j++)//串尾 71 { 72 sum[i][j]=add(i,j); 73 // printf("%d %d --> %d\n",i,j,sum[i][j]); 74 } 75 } 76 77 void work() 78 { 79 for (int i=0;i<=slen-2;i++) 80 f[i][1]=sum[0][i]; 81 for (int i=0;i<=slen-2;i++) 82 for (int j=2;j<=min(k-1,i+1);j++) 83 for (int u=j-2;u<=i-1;u++) 84 f[i][j]=max(f[i][j],f[u][j-1]+sum[u+1][i]); 85 int ans=0; 86 if (k==0) 87 ans=sum[0][slen-1]; 88 else 89 for (int i=k-1;i<=slen-2;i++) 90 ans=max(ans,f[i][k-1]+sum[i+1][slen-1]); 91 printf("%d\n",ans); 92 } 93 94 int main() 95 { 96 init(); 97 gsum(); 98 work(); 99 return 0; 100 }
P1026 統計單詞個數