[BZOJ3620]似乎在梦中见过的样子

[BZOJ3620]似乎在梦中见过的样子

Tags:题解


题意

[NOI2014]动物园:对于字符串的每个位置\(i\),求出上长度\(\le\lfloor\frac{i}{2}\rfloor\)\(Border\)数量
[BZOJ2620]似乎在梦中见过的样子:求满足存在\(Border\in[L,\lfloor\frac{i-1}{2}\rfloor]\)的位置\(i\)的数量

题解

\(Border\)显然\(KMP\)
求解KMP的nxt数组过程是不能修改的!!!否则会对后面的求解产生影响

由于答案也是\(Border\),可以考虑类似\(KMP\)递推答案的\(pos\),记录\(pos[i]\)表示做到第i个位置时被算入答案(或恰好没有被算进答案)的位置

类似于\(KMP\)的过程,如果在\(i-1\)位置由于\(Border\)过长,位置\(k\)没有被算入答案,那么在\(i\)位置时位置\(k+1\)肯定也不能被算入答案。也就是说若当前\(Border\)过长,在\(i\)位置不是答案,那么在\(i+1....n\)位置也不可能是答案,于是直接跳\(nxt\)直到小于上界

复杂度用势能分析可得是\(\mathcal{O} (n)\),因为每次\(pos\)最多也只会往后移一位
当然这题要做\(n\)遍所以是\(\mathcal{O} (n^2)\),不晓得为什么跑得过去。。。

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;
const int N=2e4;
int n,L,pos[N],nxt[N],ans;
char s[N];
void calc(int bs,int l)
{
    for(int i=2;i<=l;i++)
    {
        int j=nxt[i-1],k=pos[i-1];
        while(s[i+bs]!=s[j+1+bs]&&j) j=nxt[j];
        nxt[i]=s[i+bs]==s[j+1+bs]?j+1:j;
        while(k&&(s[i+bs]!=s[k+bs+1]||(k+1)*2>=i)) k=nxt[k];
        k+=s[i+bs]==s[k+bs+1];pos[i]=k;
        if(k>=L&&k*2<i) ans++;
    }
}
int main()
{
    scanf("%s%d",s+1,&L);
    n=strlen(s+1);
    for(int i=0;i<n;i++) calc(i,n-i);
    cout<<ans<<endl;
}
posted @ 2019-01-25 22:16  饕餮传奇  阅读(886)  评论(0编辑  收藏  举报