zoukankan      html  css  js  c++  java
  • 单词背诵

    原题链接

    题目大意:

    给出n个单词和有m个单词的文章,问其中包含这n个单词的数量(重复算一个),和包含最多单词数量下的最短连续段。
    n<=1000,m<=100000
     

    简略思路

    首先,想到的肯定是hash+暴力
    时间复杂度O(m²)
    仔细读题,
    假设右边界从右往左移动,当一个右边界对应的最小左边界找到后,再向左移动右边界,相应左边界不可能向右移动。以此可以去掉许多多余的循环。
    双指针扫一扫就行了
     
    已经懂得大佬默默离场
     
    具体来说,

    题目要求

    给定n个单词和一个长为m的段落,要求在该段落内找出包含给定单词种类数最多且最短的最优段落,求该段落包含给定单词的种类数和段落长度。

    思路分析

    为了方便叙述,我们约定用数组a[ ]来存储给定的n个单词(将其称为目标单词),用数组b[ ]来按照顺序存储给定的m段内的m个单词。

    初步思路:

    大致思路应该是很好想的,差不多就是对段落整体扫描一遍来求最优段落。首先用Hash来记录n个给定单词和m个段落内单词,然后对比得出给定段落内包含了多少种目标单词,最后对段落整体扫描一遍来寻得最优段落。

    那么重点是,如何对段落整体扫描呢?

    我们在Hash时首先对段落扫描做好预先处理:对a[ ]内单词Hash时记录a[ ]的Hash值,为了方便与b[ ]比较,我们将其用bool数组存储(这样就相当于用数组来表示该Hash值是否出现过)。如果这个地方没看懂的话,去看一下下面的代码应该就理解了。对b[ ]内单词Hash时,我们用vis[]数组来对其是否为目标单词做标记,如果该单词的Hash值在a[ ]中已经出现过,说明该单词是目标单词之一,则将其对应的vis[ ]标记为1,同时用cnt记录出现的目标单词的种类数。

    完成了预先处理,接下来我们来讨论如何对段落进行扫描求解最优段落。

    我们采用枚举区间左右端点的方法来对段落进行扫描。这里我是从最右端向左开始枚举区间。如果觉得从左开始枚举更好理解的话,翻到我的题解底部~

    我们首先对左端点进行移动。移动左端点的同时,我们使用appear[]记录一下目标单词在当前l,r包含的段落内出现的次数。如果该单词是初次出现,那么cnt--(或者再重新定义一个变量,出现一种++一下,最后比较是否和cnt相等也可),同时appear[ ]++(不是初次出现也要加,键见代码)。如果cnt=0了,那么说明当前我们枚举的段落已经包含了所有种类的b[ ]内目标单词。这时首先我们更新ans值来存储当前的最优解,然后对右端点进行操作。然后如果当前我们的右端点上的单词不是目标单词,那么我们就把它舍去,即r--;如果右端点上的单词在当前枚举段落内已经重复出现过,那么我们也可将其舍去。如果右端点上的单词是目标单词且仅出现一次,那么我们把它舍去的时候就再将cnt++,然后进行下一次扫描。这个地方是最难理解的,如果想不明白的话手动模拟一下。

    最后附上代码(已包含注解):

    #include<algorithm>
    #include<iostream>
    #include<iomanip>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    typedef long long ll;
    const int p=909043; 
    int base=313;
    int n,m,cnt,ans=1<<30,l,r;
    char s[33];
    int a[100003],b[100003];
    int appear[5000003],vis[5000003],pre[5000003];
    //pre[]记录灵梦要背的单词
    //vis[]记录灵梦要背的单词在给出的段落中是否出现
    //appear[]记录要背的单词在目前搜索到的段落中出现的次数 
    int hashs(char qwq[])
    {
        int len=strlen(qwq);
        int sum=0;
        for(int i=0;i<len;i++)
        {
            sum=(sum*base+qwq[i])%p;
        }
        return sum;
    }
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            cin>>s;
            a[i]=hashs(s);
            pre[a[i]]=1;
        }
        scanf("%d",&m);
        for(int i=1;i<=m;i++)
        {
            cin>>s;
            b[i]=hashs(s);
            if(pre[b[i]]&&!vis[b[i]])
            {
                vis[b[i]]=1;
                cnt++;
            }
        }
        if(cnt==0)
        {
            printf("0
    0");//一定要记得讨论这种情况QAQQ 
            return 0;
        }
        else printf("%d
    ",cnt);
        l=m;//从右向左扫 
        r=m;
        while(true)
        {
            if(!cnt)//cnt==0时对答案进行更新 
            {
                while(!vis[b[r]]) r--;
                //如果区间右端点不是灵梦要背的单词就将段落长度缩小 
                ans=min(ans,r-l);//想想为什么不是(r-l+1)呀qwq    
                if(appear[b[r]]>=1) 
                {
                    if(appear[b[r]]==1)
                    {
                        cnt++;
                    }
                    appear[b[r]]--;
                    r--;
                }
                //如果右端点的单词已经在段落里出现过了,就可以缩小段落长度
                //如果右端点的单词只出现了一次,那么我们将右端点向左移的同时要cnt++(因为这种单词在当前枚举段落内已经不再出现啦) 
            }
            else
            {
                if(l==0) break; 
                if(vis[b[l]])
                {
                    if(!appear[b[l]])//如果这种单词在当前枚举的段落里初次出现,那么就说明当前段落内的种类数加一 
                    {
                        cnt--;
                    }
                    appear[b[l]]++;//该种单词出现的次数增加 
                }
                l--;
            }
        }
        printf("%d",ans);
        return 0;
    }
     
     
     
  • 相关阅读:
    _MainTex_TexelSize
    资源处理参考
    unity 判断一个trans在不在sceen内
    DX11 绘制三角形 判断顺时针
    int型转LPCWSTR在MessageBox上显示
    sizeof struct
    buffer和cache
    DX11 三维空间: depth信息与stencil信息
    DX11 纹理的添加
    hlsl SV_POSITION
  • 原文地址:https://www.cnblogs.com/zzrblogs/p/10406725.html
Copyright © 2011-2022 走看看