zoukankan      html  css  js  c++  java
  • 【NOI2014】动物园

    题目

    近日,园长发现动物园中好吃懒做的动物越来越多了。例如企鹅,只会卖萌向游客要吃的。为了整治动物园的不良风气,让动物们凭自己的真才实学向游客要吃的,园长决定开设算法班,让动物们学习算法。
    某天,园长给动物们讲解 KMP 算法。
    园长:“对于一个字符串 S,它的长度为 L。我们可以在 O(L)的时间内,求出一个名为 next 的数组。有谁预习了 next 数组的含义吗?”
    熊猫:“对于字符串 S 的前 i 个字符构成的子串,既是它的后缀又是它的前缀的字符串中(它本身除外),最长的长度记作 next[i]。”
    园长:“非常好!那你能举个例子吗?”
    熊猫:“例 S 为 abcababc,则 next[5]=2。因为 S 的前 5 个字符为 abcab,ab既是它的后缀又是它的前缀,并且找不到一个更长的字符串满足这个性质。同理,还可得出 next[1] = next[2] = next[3] = 0,next[4] = next[6] = 1,next[7] = 2,next[8] = 3。”
    园长表扬了认真预习的熊猫同学。随后,他详细讲解了如何在 O(L)的时间内求出 next 数组。
    下课前,园长提出了一个问题:“KMP 算法只能求出 next 数组。我现在希望求出一个更强大 num 数组——对于字符串 S 的前 i 个字符构成的子串,既是它的后缀同时又是它的前缀,并且该后缀与该前缀不重叠,将这种字符串的数量记作num[i]。例如 S 为 aaaaa,则 num[4] = 2。这是因为 S 的前 4 个字符为 aaaa,其中a 和 aa 都满足性质‘既是后缀又是前缀’,同时保证这个后缀与这个前缀不重叠。
    而 aaa 虽然满足性质‘既是后缀又是前缀’,但遗憾的是这个后缀与这个前缀重叠了,所以不能计算在内。同理,num[1] = 0,num[2] = num[3] = 1,num[5] = 2。 ”
    最后,园长给出了奖励条件,第一个做对的同学奖励巧克力一盒。听了这句话,睡了一节课的企鹅立刻就醒过来了!但企鹅并不会做这道题,于是向参观动物园的你寻求帮助。你能否帮助企鹅写一个程序求出 num 数组呢?
    特别地,为了避免大量的输出,你不需要输出 num[i] 分别是多少,你只需要输出∏ (num[i] + 1) 对 1,000,000,007 取模的结果即可。
    其中∏ (num[i] + 1)=1= (num[1] + 1) × (num[2] + 1) × ⋯ × (num[L] + 1)。

    分析

    在kmp的基础上增加一个数组sum,表示j经过多少次next[j]后变成零。
    通过研究next数组的意义,发现sum[j]就是字符串 S 的前j个字符构成的子串,既是它的后缀同时又是它的前缀的数量,那么由于要求不重叠,那么将j一直next[j]直到j*2<=i位置。

    #include <cmath>
    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <algorithm>
    #include <queue>
    const int maxlongint=2147483647;
    const int mo=1000000007;
    const int N=1010000;
    using namespace std;
    long long next[N]={0},n,m,ans;
    long long sum[N];
    char s[N];
    int kmp()
    {
        memset(next,0,sizeof(next));
        memset(sum,0,sizeof(sum));
        int j;
        j=0;
        sum[1]=1;
        for(int i=2;i<=n;i++)
        {
            while(j && s[j+1]!=s[i]) j=next[j];
            if(s[j+1]==s[i]) j++;
            next[i]=j;
            sum[i]=sum[j]+1;
        }
        j=0;
        for(int i=2;i<=n;i++)
        {
            while(j && s[j+1]!=s[i]) j=next[j];
            if(s[j+1]==s[i]) j++;
            while(j*2>i) j=next[j];
            ans=ans*(sum[j]+1)%mo;
        }
    }
    int main()
    {
        scanf("%lld
    ",&m);
        while(m--)
        {
            scanf("%s
    ",s+1);
            n=strlen(s+1);
            ans=1;
            kmp(); 
            printf("%lld
    ",ans);
        }
    }
    
  • 相关阅读:
    界面控件DevExpress WPF入门 表达式编辑器功能
    Telerik UI for WPF全新版本——拥有Office2019高对比度主题
    DevExpress报表控件v21.2 全新的Visual Studio报表设计器
    报告生成器FastReport .NET入门指南 在Linux中启动应用程序
    文档控件DevExpress Office File API v21.2 自定义字体加载引擎
    UI组件库Kendo UI for Angular入门 如何开始使用图表功能
    WPF界面工具Telerik UI for WPF入门级教程 设置一个主题(二)
    DevExtreme初级入门教程(React篇) TypeScript支持
    报表开发利器FastReport .NET v2022.1 添加关键对象和属性
    python项目打包(一) setup.py、Python源代码项目结构
  • 原文地址:https://www.cnblogs.com/chen1352/p/9043445.html
Copyright © 2011-2022 走看看