[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)同理,之后(aa)(a)

(做完这道题之后我对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;
}
原文地址:https://www.cnblogs.com/Vikyanite/p/15458975.html