[NOIP2001 提高组] 统计单词个数
[NOIP2001 提高组] 统计单词个数
题目描述
给出一个长度不超过 \(200\) 的由小写英文字母组成的字母串(该字串以每行 \(20\) 个字母的方式输入,且保证每行一定为 \(20\) 个)。要求将此字母串分成
\(k\) 份,且每份中包含的单词个数加起来总数最大。
每份中包含的单词可以部分重叠。当选用一个单词之后,其第一个字母不能再用。例如字符串 this
中可包含 this
和 is
,选用 this
之后就不能包含
th
。
单词在给出的一个不超过 \(6\) 个单词的字典中。
要求输出最大的个数。
输入格式
每组的第一行有两个正整数 \(p,k\)。
\(p\) 表示字串的行数,\(k\) 表示分为 \(k\) 个部分。
接下来的 \(p\) 行,每行均有 \(20\) 个字符。
再接下来有一个正整数 \(s\),表示字典中单词个数。
接下来的 \(s\) 行,每行均有一个单词。
输出格式
\(1\)个整数,分别对应每组测试数据的相应结果。
样例 #1
样例输入 #1
1 3
thisisabookyouareaoh
4
is
a
ok
sab
样例输出 #1
7
提示
【数据范围】
对于 \(100\%\) 的数据,\(2 \le k \le 40\),\(1 \le s \le 6\)。
【样例解释】
划分方案为 this / isabookyoua / reaoh
【题目来源】
NOIP 2001 提高组第三题
我的解答,第一次代码提交
#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
using namespace std;
const int MAXC = 210, MAXK = 41;
int g[MAXC] = { 0 }, f[MAXC][MAXK] = { 0 };
int p, k, s;
string str;
vector<string> words;
bool cmp(string& e1, string& e2)
{
return e1.size() < e2.size();
}
void get_num_str()//得到一在i长度下有多少个单词
{
sort(words.begin(), words.end(), cmp);
for (int i = 0; i < str.size(); i++)
{
for (string word : words)
{
if (str.substr(i, word.size()) == word)
{
g[i + word.size()] += 1;
break;
}
}
}
for (int i = 1; i <= str.size(); i++)
{
g[i] += g[i - 1];
cout<<g[i]<<endl;
}
}
int main()
{
cin >> p >> k;
string temp;
for (int i = 0; i < p; i++)
{
cin >> temp;
str += temp;
}
cin >> s;
for (int i = 0; i < s; i++)
{
cin >> temp;
words.push_back(temp);
}
get_num_str();
for (int i = 1; i <= str.size(); i++)//i长度下
{
for (int j = 0; j < k; j++)//k-1个划分成为k个部分
{
for (int x = 1; x < i; x++)//a表示在i段里一分为二的位置,在str[a]所属位置的后面进行划分
{
f[i][j] = max(f[x][j - 1] + g[i] - g[x + 1], f[i][j]);
}
}
}
cout << f[str.size()][k-1];
return 0;
}
分析
这个代码能通过少部分的测试点(数据太水以至于测试点通过率能达到惊人的80%),其实这个里面有个致命的bug,就是g,g的定义就是在i长度下有多少个单词,如果想要求i-j区间的单词数目把他们相减就可以了,这种做法在特殊长度下就是失效了,比如说现在的字符串是“is”,而单词又是“is”和“s”,此时g[0]=0,g[1]=2,这时就会有歧义,g[1]-g[0]=2,但实际上现在想要表的是区间0-1的单词数目,实际上为“s”.所以这种做法必须要pass掉
如果抛弃这种投机取巧的做法,把g开成二维数组,岂不是就能消除这种歧义了?g[i][j]表示i-j区间内的所有单词数目。
得到改进代码
#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
using namespace std;
const int MAXC = 210, MAXK = 41;
int f[MAXC][MAXK] = { 0 },g[MAXC][MAXC]={0};
int p, k, s;
string str;
unordered_map<string, int> word_num;
vector<string> words;
bool check(int l, int r)
{
string temp = str.substr(l, r - l + 1);
for (string word : words)
{
if (temp.substr(0, word.size()) == word)
return true;
}
return false;
}
void get_word_num()//这里做法很巧妙,从矩阵的右下角往左上角统计数目,避免了重复统计单词数目。
{
for(int i=str.size()-1;i>=0;i--)
for (int j = i; j >= 0; j--)
{
g[j][i] = g[j + 1][i];//g[j][i]=g[j+1][i]表示[j+1,i]区间包含[j,i]区间,因此是等于这个区间的,然后再加上多的一部分就是这个区间内的所有单词数。
if (check(j, i))g[j][i]++;
}
}
int main()
{
cin >> p >> k;
string temp;
for (int i = 0; i < p; i++)
{
cin >> temp;
str += temp;
}
cin >> s;
for (int i = 0; i < s; i++)
{
cin >> temp;
words.push_back(temp);
}
get_word_num();
for (int i = 1; i <= str.size(); i++)//i长度下
{
for (int j = 1; j <= k; j++)//j个部分
{
for (int x = 0; x < i; x++)//x表示前半部分的长度
{
if(j-1<=x)
f[i][j] = max(f[x][j - 1] + g[x][i-1], f[i][j]);
}
}
}
cout << f[str.size()][k];
return 0;
}
另一种写法,哈希+dp
#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
using namespace std;
const int MAXC = 210, MAXK = 41;
int f[MAXC][MAXK] = { 0 };
int p, k, s;
string str;
unordered_map<string, int> word_num;
vector<string> words;
int get_word_num(int l, int r)
{
string temp = str.substr(l,r-l );
int ans = 0;
for (int i = 0; i < temp.size(); i++)
{
for (string word : words)
if (temp.substr(i, word.size()) == word)
{
ans++;
break;
}
}
return ans;
}
void init()
{
for (int i = 0; i < str.size(); i++)
for (int j = i; j < str.size(); j++)
word_num[str.substr(i, j - i + 1)] = get_word_num(i, j+1);
}
int main()
{
cin >> p >> k;
string temp;
for (int i = 0; i < p; i++)
{
cin >> temp;
str += temp;
}
cin >> s;
for (int i = 0; i < s; i++)
{
cin >> temp;
words.push_back(temp);
}
init();
for (int i = 1; i <= str.size(); i++)//i长度下
{
for (int j = 1; j <= k; j++)//j个部分
{
for (int x = 0; x < i; x++)//x表示前半部分的长度
{
if(j-1<=x)
f[i][j] = max(f[x][j - 1] + word_num[str.substr(x,i-x)], f[i][j]);
}
}
}
cout << f[str.size()][k];
return 0;
}
posted on 2022-09-22 20:53 jyhlearning 阅读(108) 评论(0) 编辑 收藏 举报