现在你有两个字符串,l=’0’,r=’1’。每一次操作是把l=r,r=l’+r。l’表示操作前的l。求n次操作后,所得的l中含有多少个模式串S,个数%p。
n<=10^9,|S|<=10^5,p<=10^9
我们可以发现,这是个斐波那契数列类似的串,称作斐波那契串(Yves___大神命名,%%%)。
我们要求的就是这个串的第n项。
设Vs表示s这个串中所含模式串的个数,则Vs=Vl+Vr+C,其中C是跨越区间的数量。
那么我们可以暴力用kmp求出C。l串的后|S|-1位和r串的前|M|-1位加起来做就能保证一定跨区间了。
然后,可以发现当|l|>=2*m-2的时候,C就会出现长度为2的循环节。(自己想想为什么)
那么,我们可以暴力到出现两个长度>=2*m-2的串。然后用矩阵乘法优化。
至于转移矩阵,最好开4*4的。(3*3蒟蒻表示不会打)
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define ll long long
#define M 1000005
#define N 35
using namespace std;
typedef ll arr[4][4];
char s[N][M],st[M],ch[M];
int next[M],c[N],n,m,p,a,tot;
ll ans[M],f[N];
void mult(arr a,arr b,arr d){
arr c;memset(c,0,sizeof(c));
fo(i,0,3)
fo(j,0,3)
fo(k,0,3) (c[i][j]+=a[i][k]*b[k][j]%p)%=p;
memcpy(d,c,sizeof(c));
}
int kmp() {
int ans=0,k=0;
fo(i,1,tot) {
while (k&&st[k+1]!=ch[i]) k=next[k];
k+=(st[k+1]==ch[i]);if (k==m) ans++,k=next[k];
}
return ans;
}
int main() {
scanf("%d%d%d",&n,&m,&p);
scanf("%s",st+1);int k=0;
fo(i,2,m) {
while (k&&st[k+1]!=st[i]) k=next[k];
next[i]=k+=(st[k+1]==st[i]);
}
s[0][1]='0';s[1][1]='1';f[0]=f[1]=1;
if (m==1) {
if (st[1]=='0') ans[0]=1;else ans[1]=1;
}
fo(i,2,n) {
fo(j,1,f[i-2]) s[i][j]=s[i-2][j];
fo(j,f[i-2]+1,f[i-1]+f[i-2]) s[i][j]=s[i-1][j-f[i-2]];
f[i]=f[i-2]+f[i-1];tot=0;
int x=min((ll)m-1,f[i-2]),y=min((ll)m-1,f[i-1]);
fo(j,1,x) ch[++tot]=s[i-2][f[i-2]-x+j];
fo(j,1,y) ch[++tot]=s[i-1][j];
c[i]=kmp();if (f[i-1]>2*m) {a=i;break;}
}
fo(i,2,a) ans[i]=(ans[i-2]+ans[i-1]+c[i])%p;
int y=(n-a)/2;
if (!y) {
if (n-a) printf("%lld",(ans[a-1]+ans[a]+c[a-1])%p);
else printf("%lld",ans[a]);
return 0;
}
arr g={1,1,0,0,1,2,0,0,1,1,1,0,0,1,0,1};
arr t;memcpy(t,g,sizeof(t));
for(y--;y>0;y/=2) {
if (y%2) mult(t,g,t);
mult(g,g,g);
}
f[0]=ans[a-1];f[1]=ans[a];f[2]=c[a-1];f[3]=c[a];
memset(ans,0,sizeof(ans));
fo(i,0,3) fo(j,0,3) ans[i]=(ans[i]+f[j]*t[j][i]%p)%p;
if ((n-a)%2) printf("%lld",(ans[0]+ans[1]+ans[2])%p);
else printf("%lld",ans[1]);
}