[luoguP2375] [NOI2014] 动物园

题目链接

其实题意还挺难理解的。。。

其实就是定义了一个数组,叫做 num[i]

假设字符串为 s=abcababc

那么num[4]=1, 因为在前缀子串s[1,4]=abca (我这里下标是从1开始)中后缀a与前缀a相同所以为1

假设字符串为 s=aaaa

那么num[4]=2, 虽然s[1,4]=aaaa 拥有a,aa,aaa,aaaa四个前缀,和aaaa,aaa,aa,a四个后缀
但是aaa前缀与aaa后缀有重叠部分所以不算,aaaa同理,之后aaa

(做完这道题之后我对kmp的next数组的理解大大提升了!)

首先我们先要知道next数组到底是在干嘛。
我们先从next数组的目的是什么出发,next数组是为了减少失配时的重新匹配数。
就相当于失配时,是将匹配串移动next[i]的距离而不是仅仅移动一格进行重新匹配。

为什么可以移动next[i]的距离?因为next[i]的定义就是[1,i]的子串的后缀与前缀的最长匹配长度。(因为下标从1开始,所以长度就其实是下标)

也就是说这段后缀与前缀的一段是完全相等的。

所以如果这个位置的下一个位置失配了,我们就可以跳到前缀中与这一段完全相等的位置也就是next[i],从而减少重新匹配的次数,达到线性复杂度。

于是我们重新看这道题,这道题求的是[1,i]的字符串中的后缀与前缀相同但不重叠数的后缀数。

我们只知道next[i]的定义,但是next数组跟这个似乎完全扯不上关系啊。

这里就比较思维了, 首先如果next[i]!=0的话,说明这个子串肯定有为[1,next[i]]的公共前后缀,
也肯定有[1,next[next[i]]]的公共前后缀,.....于是我们发现,这个不就是我们要求的重叠的num[i]吗,所以就是求多少次到next[i]为0。

这里可以举个例子,假设$s[1, 13] = abcabcdabcabc$, $next[13] = 6$
所以$[1, 13]$有$[1, 6]$的公共前后缀,也会有$[1, 3]$的公共前后缀。是不是很神奇!
(其实我们从next数组的定义出发就能发现这个规律了,但是我想了好久,还是太菜了www)

之后我们来解决不重叠的问题,我们考虑如何不重叠,不就是公共前后缀的长度小于等于 i/2也就是 next[j]<=i/2不就行了吗

但是暴力跳肯定会t的,于是我们考虑优化,我们是否需要每次都将指针从当前位置出发?
仔细思索了一下,j其实不需要每次都从当前i位置出发,只需要跟求next数组的时候一样就好了,当s[j+1]==s[i]的时候,就把j++,之后当他大于 i/2 的时候我们就让j=next[j]就好了。

当前的num[i]=num[j]+1

之后统计答案即可

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6+10;
char s[maxn];
int nxt[maxn], num[maxn];
const int mod = 1e9+7;
int main() {
    int T; scanf("%d", &T);
    while (T--) {
        scanf("%s", s+1);
        int n = strlen(s+1);
        num[1] = 1;
        for (int i = 2, j = 0; i <= n; ++ i) {
            while (j && s[j+1] != s[i]) j = nxt[j];
            if (s[j+1] == s[i]) j ++;
            nxt[i] = j;
            //if(j)    关于这里为什么不用这句可以想一下
            num[i] = num[j] + 1; 
        }
        long long ans = 1;
        for (int i = 2, j = 0; i <= n; ++ i) {
            while (j && s[j+1] != s[i]) j = nxt[j];
            if (s[j+1] == s[i]) j ++;
            while (j*2 > i) j = nxt[j];
            ans = ans * (num[j] + 1) % mod;
        }
        cout << ans << endl;
    }
    return 0;
}
posted @   ViKyanite  阅读(30)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
历史上的今天:
2019-10-25 计蒜客练习题:蒜头君学英语(transform)
点击右上角即可分享
微信分享提示
主题色彩