P2375 [NOI2014]动物园

P2375 [NOI2014]动物园

为什么复制题目连字体大小一块复制过来了(雾)

我竟然会做NOI的题目辣~\(≧▽≦)/~(看的题解

总而言之,这是一道简单的KMP问题。题面简直是给没学过KMP的人看的(比如我)。

我们发现,这个所谓的num数组和nxt有异曲同工之妙。但是我们对于不能重合这一块有一点问号。那我们先不管重不重合,先给他记录成重合的。

于是在标记nxt时同时也可以把num标记。原理是,nxt记录的是该字符串相同的前缀字符个数,num[i]记录的是当前字符作为从0到i的子串内后缀与前缀相同的子串的子串的数目。我们发现,其实他就是num[j]+1,j就是nxt[i]!可以举几个例子模拟一下。

这样一来查询的时候也很方便了。

问题来了,怎么去重呢?如果j已经到i<<1的时候,我们将j挪到nxt[j]就好了,直到j<i/2。因为上面我们记录的num数组的特性,如果有重叠此时的值就相当于在nxt[j]的时候的没有重叠的串的num数。模拟一下也很好理解的。

复制代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define mod 1000000007 
using namespace std;
typedef long long ll;
const int maxn=1e6+5;
char a[maxn];
int nxt[maxn],len,num[maxn];
inline void getnxt()
{
    int k=-1,j=0;
    nxt[0]=-1;
    while(j<len)
    {
        if(k==-1 or a[j]==a[k])nxt[++j]=++k,num[j]=num[k]+1;
        else k=nxt[k];
    }
}
inline void kmp()
{
    int j=0,i=1;
    ll ans=1;
    while(i<len)
    {
        if(j==-1 or a[j]==a[i]){
            j++,i++;
            while((j<<1)>=(i+1))j=nxt[j];
            ans=(ans*(ll)(num[j]+1))%mod;
        }
        else j=nxt[j];
    }
    printf("%lld\n",ans);
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%s",&a);
        len=strlen(a);
        memset(nxt,0,sizeof nxt);
        num[0]=0,num[1]=1;
        getnxt();
        kmp();
    }
    return 0;
}
复制代码

 

posted @   Star_Cried  阅读(127)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示