本题解很易懂!
给你一个数
n和一个字符串
s,求符合长度为
n且在
s中连续的字符不能连续出现,即这个字符串中不能出现
s的连续子串,答案对
1e9+7取模。
n≤1e15
不难想到一个很暴力的
dp:
设
dp[i][j]为长度为
i且当前字符为
j的合法字符串个数
(mod1e9+7),不难推式子:
dp[i][j]=k=1∑26dp[i−1][k]∣(k,j)∈s
∀1≤i≤26dp[1][i]=1
ans=i=1∑26dp[n][i]
时间复杂度
O(262n)?反正T飞。
我们珂以把上面的式子变一下,设
w[i][j]=[(i,j)∈s],即若
(i,j)在
s中不连续,则
w[i][j]=1,否则
w[i][j]=0。
式子变成了:
dp[i][j]=k=1∑26dp[i−1][k]×w[k][j]
珂能你一开始没看懂,
dp[i−1][k]×w[k][j]是啥呢?
分析一下,若
(k,j)∈s,那么
w[k][j]=1,
dp[i−1][k]×w[k][j]=dp[i−1][k],贡献为
dp[i−1][k],符合题意。
若
(i,j)∈s,则
w[k][j]=0,贡献为
0。
你可能会说“时间复杂度并没有变…”
确实没有变,但是这个式子珂以优化了。
感觉这个式子像什么?对!矩阵乘法!!!
矩乘的板子是
c[i][j]=a[i][k]×b[k][j](k≤p)
我们建
n个
1×26的矩阵
F1,F2,F3,⋯Fn,分别代表
dp[1][1⋯26],dp[2][1⋯26],dp[3][1⋯26]⋯dp[n][1⋯26]再建一个
26∗26的矩阵
A具体含义见下面。
初始化:
F1=[1111⋯1](26个1)
A=⎣⎢⎢⎢⎡w[1][1]w[2][1]⋮w[26][1]w[1][2]w[2][2]⋮w[26][2]w[1][3]w[2][3]⋮w[26][3]⋯⋯⋱⋯w[1][26]w[2][26]⋮w[26][26]⎦⎥⎥⎥⎤
不难推出
Fi=Fi−1×A(i>1)
然而模拟矩阵乘也会炸啊…
矩阵乘法是
O(263)的时间复杂度,算上递推的
O(n)就是
O(n×263),惊,被暴力吊打!
然而还有一个神器,矩阵快速幂!
(不会快速幂的珂以退出补知识了)
我们知道矩乘是有分配律的,但是这个顺推的式子好像不像是矩阵快速幂的样子…
那么开始下面一波操作:
F2=F1×A
F3=F2×A
然后我们将上面求的
F2代入:
F3=F1×A×A=F1×A2
F4=F3×A=F1×A3
⋮
Fn=F1×An−1
妙啊!
ans=i=1∑26Fn(1,i)
但是你会发现,如果只到这一步的话,最后还要算一个
F1×An−1,爆掉。
但其实,
i=1∑26Fn(1,i)=i=1∑26j=1∑26An−1(i,j)
???
什么操作?
如果您的矩阵基础扎实,请跳过此步骤。
设
C=F1×An−1,则
C(1,1)=F1(1,1)×An−1(1,1)+F1(1,2)×An−1(2,1)+⋯+F1(1,26)×An−1(26,1)
因为
F1=[1111⋯1](26个1)
所以
C(1,1)=An−1(1,1)+An−1(2,1)+⋯+An−1(26,1)
其他点以此类推
发现
C矩阵的所有数的和就是
An−1矩阵的所有数的和。
这其实是矩阵的简单的游戏。
时间复杂度
O(263logn),能过。
Code:
# include <bits/stdc++.h>
using namespace std;
struct Martix
{
long long a[30][30];
}A;
unsigned long long n;
char s[100010];
const long long mod=1e9+7;
Martix operator *(const Martix &x,const Martix &y)
{
Martix ans;
for(int i=1;i<=26;i++) for(int j=1;j<=26;j++) ans.a[i][j]=0;
for(int i=1;i<=26;i++)
{
for(int j=1;j<=26;j++)
{
for(int k=1;k<=26;k++)
{
ans.a[i][j]=(ans.a[i][j]+(x.a[i][k]%mod)*(y.a[k][j]%mod)%mod)%mod;
ans.a[i][j]%=mod;
}
}
}
return ans;
}
Martix js(Martix x,long long n)
{
Martix ans;
for(int i=1;i<=26;i++)
{
for(int j=1;j<=26;j++)
{
ans.a[i][j]=0;
}
}
for(int i=1;i<=26;i++) ans.a[i][i]=1;
while(n)
{
if(n&1)
{
ans=ans*x;
n--;
}
x=x*x;
n>>=1;
}
return ans;
}
int main(void)
{
scanf("%llu%s",&n,&s);
for(int i=1;i<=26;i++)
{
for(int j=1;j<=26;j++)
{
A.a[i][j]=1;
}
}
for(int i=1;i<strlen(s);i++)
{
A.a[s[i-1]-'a'+1][s[i]-'a'+1]=0;
}
Martix ans=js(A,n-1) ;
long long tot=0;
for(int i=1;i<=26;i++)
{
for(int j=1;j<=26;j++)
{
tot=(tot%mod)+(ans.a[i][j]%mod)%mod;
}
}
cout<<tot%mod<<endl;
return 0;
}