你有l和r两个字符串,初始l=”0”,r=”1”,每轮操作定义如下。
将r变成原来的l+原来的r,这里的+表示将两个字符串连接起来
将l变成原来的l
给定一个长度为m的匹配串s,问操作n次后,l中包含了多少个s作为子串,答案对p取模。
n≤109,m≤104,0<p≤109
首先这道题目就是一个斐波拉契形式的字符串组合。
刚做这道题的时候,找到了一个字符串的规律,以为这是具有普遍性的,然后打了个矩阵……后来发现错了,最后还是打了个暴搜30分。
其实。当时可以进一步的思考。
首先我们可以发现这道题目的m并不是很大。
然后字符串加入的规则是上一个的r+l那么匹配串的个数也是r的答案加上l的答案,再加上因为拼接而产生的答案。然而我们只需要把r的后m-1个和l的前m-1个加起来,做一次KMP就好了。
再考虑m,我们发现当串的长度比2m-2大时头和尾基本就不变了,那么中间贡献的答案在模p意义下是有循环节的,可以发现这个循环节为2。
然后建立矩阵,矩阵快速幂就好了。
#include
#include
#include
#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
typedef long long ll;
using namespace std;
int i,j,k,l,t,n,m,p,r,next[30000],c[1000007],tot,ans1[1000007];
ll ans[1000007];
char s[40][1000007],st[1000007],sr[1000007];
struct node{
ll ju[4][4];
node friend operator *(node x,node y){
int i,j,k;
node z;
memset(z.ju,0,sizeof(z.ju));
fo(i,0,3){
fo(j,0,3){
fo(k,0,3){
z.ju[i][j]=(x.ju[i][k]*y.ju[k][j]%p+z.ju[i][j])%p;
}
}
}
return z;
}
}a,b;
node qsm(node x,int y){
node z;
memset(z.ju,0,sizeof(z.ju));
int i;
fo(i,0,3)z.ju[i][i]=1;
while(y!=0){
if(y&1!=0)z=z*x;
x=x*x;
y/=2;
}
return z;
}
int main(){
scanf("%d%d%d",&n,&m,&p);
scanf("%s",st+1);
fo(i,2,m){
while(j&&st[i]!=st[j+1])j=next[j];
if(st[i]==st[j+1])j++;
next[i]=j;
}
s[0][1]='0';s[1][1]='1';c[0]=c[1]=1;
fo(i,2,n){
fo(j,1,c[i-2])s[i][++c[i]]=s[i-2][j];
fo(j,1,c[i-1])s[i][++c[i]]=s[i-1][j];
tot=0;
int u=min(m-1,c[i-2]),v=min(m-1,c[i-1]);
fo(j,1,u)sr[++tot]=s[i-2][c[i-2]-u+j];
fo(j,1,v)sr[++tot]=s[i-1][j];
k=t=0;
fo(j,1,tot){
while(k&&sr[j]!=st[k+1])k=next[k];
if(sr[j]==st[k+1])k++;
if(k==m)ans1[i]=(ans1[i]+1)%p,k=next[k];
}
if(c[i-1]>2*m)break;
}
t=i;
if(m==1){
if(st[1]=='0')ans[0]=1;else ans[1]=1;
}
fo(i,2,t){
ans[i]=(ans[i-2]+ans[i-1]+ans1[i])%p;
}
if(t==n){
printf("%lld\n",ans[n]);
return 0;
}
b.ju[0][0]=1;b.ju[0][1]=1;b.ju[0][2]=0;b.ju[0][3]=0;
b.ju[1][0]=1;b.ju[1][1]=2;b.ju[1][2]=0;b.ju[1][3]=0;
b.ju[2][0]=1;b.ju[2][1]=1;b.ju[2][2]=1;b.ju[2][3]=0;
b.ju[3][0]=0;b.ju[3][1]=1;b.ju[3][2]=0;b.ju[3][3]=1;
memset(a.ju,0,sizeof(a.ju));
a.ju[0][0]=ans[t-1];a.ju[0][1]=ans[t];
a.ju[0][2]=ans1[t-1];a.ju[0][3]=ans1[t];
l=n-t;
b=qsm(b,l/2);
a=a*b;
if(l%2){
printf("%lld\n",(a.ju[0][0]+a.ju[0][1]+a.ju[0][2])%p);
}
else{
printf("%lld\n",a.ju[0][1]);
}
}