【NOI2014】BZOJ3670动物园题解(KMP算法)

题目:BZOJ3670.
题目大意:给定一个长度为 n n n的字符串,求这个字符串每一个前缀 S [ 1.. i ] S[1..i] S[1..i]的既是前缀又是后缀且这两个前后缀不能重叠的子串的数量 n u m [ i ] num[i] num[i],并输出所有 ( n u m [ i ] + 1 ) (num[i]+1) (num[i]+1)的乘积.
1 ≤ n ≤ 1 0 6 1\leq n\leq 10^6 1n106,数据组数 ≤ 5 \leq 5 5.

这道题是道语文题,题面又臭又长而且 n u m [ i ] num[i] num[i]的定义是数量不是最长长度,在题面理解上直接是被坑许久啊…

首先考虑没有前后缀不重叠这个条件的情况下该如何计数,考虑对于一个前缀 S [ 1.. i ] S[1..i] S[1..i],它既是前缀又是后缀的子串其实就只包括 S [ 1.. i ] , s [ 1.. n e x t [ i ] ] , S [ 1.. n e x t [ n e x t [ i ] ] ] , . . . S[1..i],s[1..next[i]],S[1..next[next[i]]],... S[1..i],s[1..next[i]],S[1..next[next[i]]],...那么数量也就是这样迭代下去可以进行的数量.

所以我们考虑记录 c n t [ i ] cnt[i] cnt[i]表示前缀 S [ 1.. i ] S[1..i] S[1..i]既是前缀又是后缀的子串数量,那么很明显 c n t [ i ] = 1 + c n t [ n e x t [ i ] ] cnt[i]=1+cnt[next[i]] cnt[i]=1+cnt[next[i]].

考虑有了前后缀不重叠的限制后的计算,也就是 n u m num num数组的计算,我们可以发现只需要计算出每一个前缀的最长的既是前缀又是后缀且前后缀不重叠的字串长度 n e x t 2 [ i ] next2[i] next2[i],我们就可以计算出 n u m [ i ] = c n t [ n e x t 2 [ i ] ] num[i]=cnt[next2[i]] num[i]=cnt[next2[i]].

发现那个 n e x t 2 next2 next2数组计算其实可以仿照 n e x t next next数组的计算,只是要在 n e x t next next计算的基础上多加一个条件 n e x t 2 [ i ] ∗ 2 &lt; = i next2[i]*2&lt;=i next2[i]2<=i就可以了.

代码如下:

#include<bits/stdc++.h>
  using namespace std;
 
#define Abigail inline void
typedef long long LL;
 
const int N=1000000;
const LL mod=1000000007;
 
char a[N+9];
int n,nxt[N+9],nxt2[N+9];
LL ans,cnt[N+9];
 
Abigail into(){
  scanf("%s",a+1);
  n=strlen(a+1);
}
 
Abigail work(){
  ans=1LL;
  int j=0,j2=0;
  nxt[1]=0;nxt2[1]=0;cnt[1]=1;
  for (int i=2;i<=n;++i){
    while (a[i]^a[j+1]&&j>0) j=nxt[j];
    if (a[j+1]==a[i]) ++j;
    nxt[i]=j;
    while (a[i]^a[j2+1]&&j2>0) j2=nxt[j2];
    if (a[j2+1]==a[i]) ++j2;
    while (j2<<1>i) j2=nxt[j2];
    nxt2[i]=j2;
    cnt[i]=1+cnt[nxt[i]];
    ans=ans*(cnt[nxt2[i]]+1)%mod;
  }
}
 
Abigail outo(){
  printf("%lld\n",ans);
}
 
int main(){
  int T;
  scanf("%d",&T);
  while (T--){
    into();
    work();
    outo();
  }
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值