洛谷2375 BZOJ3670 NOI2014动物园 KMP

题目链接
题意:
给你一个长度为n的字符串,求对于每一个1到n之间的i,求字符串1到i的不重叠且相同的前缀与后缀的个数 n u m [ i ] ,输出 i = 1 n ( n u m [ i ] + 1 )   的结果。

题解:
根据题目的提示,很容易往KMP方向想。我们发现,只有next[i],next[next[i]]…这些位置会在字符串1-i中出现前缀与后缀相同的情况。
我们先不管不能重叠的限制,我们可以先求出可以重叠的情况,具体就是num[i]=num[next[i]]+1。然后我们再考虑不能重叠的限制,也就是对于前缀的末尾j,要满足 j < i 2
但是我们每一个i暴力往前跳next数组,直到跳到一个满足的j,复杂度是不对的,会被那种全是一种字符的字符串卡成 O ( n 2 ) ,所以我们考虑这其中有很多次我们也是在做完之前的处理就知道可以不用重复跳的,这也是这题的巧妙之处,就是我们可以在满足这个j的限制的时也向KMP那样跳过那些已经知道不需要再跳的位置。这样做的正确性似乎没大有人清楚的解释了,我的理解是跳到了一个当前i的第一个合法位置j之后,i+1的next要跳到一个不大于 i + 1 2 的第一个合法位置肯定是不会出现在j+1之后的,因为i的时候某个位置k如果不行,不可能在i+1时可能在k+1可以,因为只有出现了i位置的合法情况,后面一个也和i+1对应,才可能出现i+1的合法位置,都没出现过合法的i的对应j,更不可能出现i+1的了,所以接着上一个j继续做类似KMP的操作是正确的。

代码:

#include <bits/stdc++.h>
using namespace std;

int T,n,mod=1000000007,nxt[1000010],dep[1000010],num[1000010];
char s[1000010];
long long ans=1;
inline void getnext()
{
    int j=0;
    nxt[1]=0;
    dep[1]=1;
    for(int i=2;i<=n;++i)
    {
        while(j&&s[j+1]!=s[i])//ÕâÀïi¸Õ¼Ó¹ý£¬Ö®Ç°jºÍi-1Æ¥ÅäÁË 
        j=nxt[j];
        if(s[j+1]==s[i])//µ±Ç°jÌøµ½ÁËÓëiÏàͬµÄ 
        ++j;
        nxt[i]=j;
        dep[i]=dep[j]+1;
    }
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        ans=1;
        scanf("%s",s+1);        
        n=strlen(s+1);
        getnext();                  
        int j=0;
        for(int i=2;i<=n;++i)
        {
            while(j&&s[j+1]!=s[i])
            j=nxt[j];
            if(s[j+1]==s[i])
            ++j;
            while((j<<1)>i)
            j=nxt[j];
            ans=(long long)(ans*(dep[j]+1))%mod;
        }       
        printf("%d\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/forever_shi/article/details/81322387