题目链接 | 难度等级 | 完成状态 | 完成分数 | 最后编辑时间 | 失误原因(初次提交分数) |
---|---|---|---|---|---|
统计单词个数 | ★★☆☆☆ | 答案正确 | 100 | 2015-2-3 17:32:11 | 算法错误(60) |
将字母串分成k份,每份中包含的单词个数加起来总数最大(单词可以部分重叠,第一个字母位置不能再用)
第一个小问题是如何求出第j~i位包含的单词总数,记作Count[j][i]。容易得到方程
Count[j][i]=max { Count[j][i-1]+1 //如果以索引i结束的有一个单词// Count[j][i-1] //如果没有// };
最初想法是采用前缀数组来计算,但是会遗漏某些重叠等情况,特别是某些单词是其它单词前缀的情况(直接去除较短了好像由于数据原因能A但是明显有反例)。误入歧途了很多次。
第二个小问题是求解。记f[i][k]为索引0~i处划分k次取得的最优解。枚举划分点,容易得到方程
f[i][k]=max{f[j][k-1]+Count[j+1][i]};
(实际可以再省去一维空间,但是数据量较小,而且便于理解保留k维度)
1040.cpp代码已折叠
展开折叠内容
|
---|
#include<iostream> #include<cstring> #include<string> using namespace std; int T,L,K,W,Count[300][3090],f[300][300]; string w[10]={},str; int main() { cin>>T; while(T--) { str=""; cin>>L>>K; for(int i=1;i<=L;++i) { string s; cin>>s; str+=s; } cin>>W; for(int i=1;i<=W;++i) cin>>w[i]; memset(Count,0,sizeof(Count)); memset(f,-2,sizeof(f)); for(int i=0;i<str.size();++i) for(int j=0;j<=i;++j) { if(i>=1&&i>j) Count[j][i]=max(Count[j][i],Count[j][i-1]); for(int t=1;t<=W;++t) { if(i-(signed)w[t].size()+1>=j) { if(str.substr(i-w[t].size()+1,w[t].size())==w[t]) { if(i>j&&i>=1) Count[j][i]=max(Count[j][i],Count[j][i-1]+1); else Count[j][i]=max(Count[j][i],1); } } } f[i][1]=Count[0][i]; } for(int i=0;i<str.size();++i) for(int j=0;j<i;++j) for(int k=2;k<=K;++k) { f[i][k]=max(f[i][k],f[j][k-1]+Count[j+1][i]);//fixed:f[j][k]->f[j][k-1]// } cout<<f[str.size()-1][K]<<endl; } return 0; } |