[NOI2014] 动物园 (kmp)
作者:互联网
浅讲一下思路
1、最开始呢,并没有意识到问题的严重性。认为num与nxt是一样的,再加上一个不重叠的条件即可。然后就发现样例都过不去,还觉得自己可对了。再次读题发现num是字符串的数量,于是又加上了nxt
inline void get_nxt(const string s){ int j=0;nxt[0]=num[0]=0; for(int i=1;i<len;++i){ while(j&&(s[i]!=s[j]||j>(i+1)/2-1)) j=nxt[j-1]; cout<<j<<endl; if(s[i]==s[j]&&j<(i+1)/2) num[i]=num[++j]+1; nxt[i]=j; } }
当然会wa,为什么呢?num其实只存了对于当前i不重叠的子串,然后就向后转移了。But,当前重叠不代表以后还重叠,因此num并不是单纯的线性关系
2、接下来抱着无奈的心情,直接暴力寻找nxt的“层数”。那么为什么公共前后缀的个数就是nxt的“层数”呢?其证明与nxt相似
(手残见谅)
看得出num【i】=3,那这个3怎么来的?
首先找到第一个nxt[j]<=(i+1)/2,j=3,所以A=B,这是第一个串‘aaa’。继续向前找,nxt【2】也符合条件(其实从此刻向前所有nxt都符合),由A=B得C=D,由nxt【2】=2得C=E,因此E=D,这是第二个串‘aa’。同理可得第三个串‘a’。
inline void get_nxt(const string s){ int j=0;nxt[0]=0; for(int i=1;i<len;++i){ while(j&&s[i]!=s[j]) j=nxt[j-1]; if(s[i]==s[j]) ++j; nxt[i]=j;int k=j; while(k){ if(k<=(i+1)/2) num[i]++; k=nxt[k-1]; } } }
十分不出意外,T掉了。因为无法存储num,我们陷入僵局
3、老师让把这道题当例题看,赶紧滚去看了题解,惊讶的发现num可以分开存储和查询
关键来了
如2步中,找到第一个nxt[j]<=(i+1)/2后,所有j=nxt【j-1】都符合。那我们就可以学着kmp,减少重复的搜索。
初始化num【0】=1,表示‘a’是他自己的公共前后缀,也表示此刻为nxt第一层。显然,num【i】并不为前i个字符构成的子串不重复的公共前后缀的个数。那怎么找个数呢?
仍然是找第一个j<=(i+1)/2,但此时num【j-1】,也就是nxt【j】的层数,就是我们所求
上代码
#include<bits/stdc++.h> using namespace std; const int mod=1e9+7; int n,len,j,nxt[1000010],num[1000010]; string s;long long ans; inline void get_nxt(const string s){ j=0;num[0]=1; for(int i=1;i<len;++i){ while(j&&s[i]!=s[j]) j=nxt[j-1]; if(s[i]==s[j]) ++j; nxt[i]=j;num[i]=num[j-1]+1; } } int main() { cin>>n; while(n--){ cin>>s;ans=1; len=s.size();get_nxt(s); for(int i=0;i<len;++i){ j=nxt[i]; while((j<<1)>i+1) j=nxt[j-1]; ans=ans*(num[j-1]+1)%mod; } cout<<ans<<endl; } }
另外,num【j-1】中j可能为0,j-1会越数组下标,O2会爆0。当然,不建议这样写。其实是我懒得改了
标签:nxt,const,string,get,int,NOI2014,num,kmp,动物园 来源: https://www.cnblogs.com/yswn/p/16518978.html