1668 非010串
基准时间限制:1 秒 空间限制:131072 KB 分值: 80 难度:5级算法题 收藏 关注
如果一个01字符串满足不存在010这样的子串,那么称它为非010串。
求长度为n的非010串的个数。(对1e9+7取模)
Input
一个数n,表示长度。(n<1e15)
Output
长度为n的非010串的个数。(对1e9+7取模)
Input示例
3
Output示例
7
解释:
000
001
011
100
101
110
111
见到这题时,是去年的算法马拉松,当时自己不会,问学长~~也不会, 讨论了一顿,还是不会。。。。今年为了准备邀请赛重新开刷了51nod,弱鸡表示很无力。。。。。突然想起这题,就跑去看题了,发现切入的时候思路还是挺清晰的。。。。。
一开始看错题目,以为求非01串,觉得这题莫名简单啊。。。。。推了个递推式
f[n]=2f[n-1]-f[n-3],
然后看数据是1e15,马上开心的写了一个矩快。。。。
一交,全WA,
一看题发现自己看错了,,,两种情况的递推公式还是不一样的,,,,
思路:要计算长度为n的非010串的数目???看到长度为n,我想应该dp吧??
为什么想到dp?
xxx010 一定是从 xxx01转移得到的,
xxx01一定是从xxx0转移得到的,
xxx0,一定是由xxx转移得到得
确立了以上的关系,就能得出状态转移式,然后求解
把非010串定义为合法串,即其他的是非法串
那就看看Fn-1(长度为n-1时,合法串的数量)和Fn(长度为n时,合法串的数量)有什么关系,
约定:
Fn:长度为n的合法串数量
Sn:长度为n,末尾两位是01的合法串数量
An:长度为n,末位是0的合法串数量
1。首先发现Fn的上界应该是2*Fn-1:每个长度为n-1的合法串, 都能变成2个长度为n的字符串(可能是合法串也可能是非法串)
2。显然发现1中存在一条不确定关系,到底如何确定,n-1的合法串转化的是长度n的合法串还是非法串?
观察一下就很容易发现,“010”一定是由“01”+“0”转化而来的,这样一来1中不确定的关系就确定了:
长度为n的合法串的数量=长度为n-1时的合法串的数量*2-长度为n-1后两位是01的合法串 即:Fn=2*Fn-1 - Sn-1
那么Sn?
长度为n后两位是01的合法串 = 长度为n-1末位是0的合法串
Sn=An-1
那么:An?
长度为n末位是0的合法串=长度为n-1合法串-长度为n-1末尾是01合法串
An=Fn-1-Sn-1
为什么要减去Sn-1?因为01+0=010,这是非法的字符串,要去掉
那这样所有的递推关系就明了了:
Fn:长度为n的合法串数量
Sn:长度为n,末尾两位是01的合法串数量
An:长度为n,末位是0的合法串数量
Fn=2*Fn-1 - Sn-1
Sn=An-1
An=Fn-1-Sn-1
由上面可知,只要有F1,S1,A1,就一定能递推得到Fn,Sn,An
矩阵快速幂:
2 -1 0 Fn-1 Fn
0 0 1 * Sn-1 = Sn
1 -1 0 An-1 An
代码:
#include <iostream>
using namespace std;
typedef long long ll;
struct mt{
ll m[10][10];
ll h,l;
};
ll md=1e9+7;
mt mul(mt a,mt b){
mt res;
res.h=a.h,res.l=b.l;
for(int i=0;i<a.h;i++){
for(int j=0;j<b.l;j++){
res.m[i][j]=0;
}
}
for(int i=0;i<a.h;i++){
for(int j=0;j<b.l;j++){
for(int x=0;x<a.l;x++){
res.m[i][j]= (res.m[i][j]%md+(a.m[i][x]%md)*(b.m[x][j]%md)%md+md)%md;
}
}
}
return res;}
mt quick_mod(mt a,mt b,ll n){
mt res=b;
while(n){
if(n&1){
res=mul(a,res);
}
a=mul(a,a);
n=n>>1;
}
return res;
}
int main()
{
ll n;
cin>>n;
if(n==0){
cout<<"0"<<endl;
return 0;
}
if(n==1){
cout<<"2"<<endl;
return 0;
}
n=n-1;
mt a,b;
a.h=a.l=3;
b.h=3;
b.l=1;
a.m[0][0]=2, a.m[0][1]=0,a.m[0][2]=-1,
a.m[1][0]=1,a.m[1][1]=0;a.m[1][2]=-1;
a.m[2][0]=0,a.m[2][1]=1;a.m[2][2]=0;
b.m[0][0]=2,b.m[1][0]=1; b.m[2][0]=0;
mt res=quick_mod(a,b,n);
cout<<res.m[0][0]<<endl;
return 0;
}