首页 > 其他分享 >[NOIP2001 提高组] 统计单词个数

[NOIP2001 提高组] 统计单词个数

时间:2022-09-22 21:00:42浏览次数:43  
标签:word NOIP2001 temp int 个数 单词 str string

[NOIP2001 提高组] 统计单词个数

题目描述

给出一个长度不超过 \(200\) 的由小写英文字母组成的字母串(该字串以每行 \(20\) 个字母的方式输入,且保证每行一定为 \(20\) 个)。要求将此字母串分成
\(k\) 份,且每份中包含的单词个数加起来总数最大。

每份中包含的单词可以部分重叠。当选用一个单词之后,其第一个字母不能再用。例如字符串 this 中可包含 thisis,选用 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;
}

标签:word,NOIP2001,temp,int,个数,单词,str,string
From: https://www.cnblogs.com/jyhlearning/p/16720801.html

相关文章

  • 洛谷 P1025 [NOIP2001 提高组] 数的划分 (dfs)
    https://www.luogu.com.cn/problem/P1025给定一个n和k,把n拆分成k个数字的和,数字可以相同,但是种类不能相同。求能凑出的数量。输入73输出4明明是一道很简单的dfs,......
  • C/C++ 使用四种方法交换两个数(包括不使用第三个变量)
    #include<iostream>#include<string>#include<windows.h>usingnamespacestd;//方式一使用指针voidswap1(int*a,int*b){//指针作为函数的参数intt......
  • 普通莫队解决区间众数的个数
    SP32900KDOMINO-K-dominantarrayP1997faebdc的烦恼P3709大爷的字符串题被摩尔投票法洗脑半天,发现好像可以直接拿莫队写啊喂!针对原数据离散化,后开一个计数器和一......
  • T1046判断一个数能否同时被3和5整除 (信息学一本通C++)
     目录 [题目描述] 判断一个数n能否同时被3和5整除,如果能同时被3和5整除输出YES,否则输出NO。[输入]输入一行,包含一个整数n。( -1,000,000<n<1,000,000)[输出]......
  • 获取一个数组中的最大值和最小值三种方法
    1遍历数组int[]nums={3,1,5,2,4};intmax=nums[0];intmin=nums[0];for(inti=1;i<nums.length;i++){max=Math.max(max,nums[i]);min=Ma......
  • Swift — LeetCode — 两个数组的交集 II
    我正在参加“掘金·帆船计划”话题给你两个整数数组数字1和数字2,请将两个数组的交集作为数组返回。返回结果中每个元素的出现次数应与该元素在两个数组中出现的次数......
  • 用python从网页下载单词库
    从网站下载单词库1每一页有几百个单词2每一个单词有独立的URL,URL中包含单词的中文解释3使用的库requests,pyquery,web#coding:utf-8importrequestsasrqfrom......
  • 516 筛法求约数个数
    视频链接:#include<iostream>usingnamespacestd;constintN=1000010;intp[N],vis[N],cnt;inta[N];//a[i]记录i的最小质因子的次数intd[N];//d[i]记录i......
  • [NOIP2000 提高组] 单词接龙
    [NOIP2000提高组]单词接龙题目背景注意:本题为上古NOIP原题,不保证存在靠谱的做法能通过该数据范围下的所有数据。题目描述单词接龙是一个与我们经常玩的成语接龙相......
  • JavaScript合并多个数组
    工作中经常会对数组进行合并,稍微总结一下常用的方法:concatJavaScript原生自带的函数,用法如下:letarr1=[3,5,7];letarr2=[4,78,79];letarr3=[];arr3=......