题意:
有两个字符串S,T。
满足:
l
e
n
S
=
N
len_S=N
lenS=N
l
e
n
T
=
M
len_T=M
lenT=M
T是S的子串
字符串集合大小为A。
求方案数mod 1000000007
N
⩽
200
,
M
⩽
50
,
A
⩽
1
0
9
N\leqslant 200,M\leqslant 50,A\leqslant 10^9
N⩽200,M⩽50,A⩽109
解法
首先有一个跟这题没有多大关系的结论:
一个长度为n的字符串的border集合的一个上界是
O
(
n
2
l
o
g
2
n
)
O(n^{2log_2n})
O(n2log2n)级别的。
其实,令c为黄金分割,我们可以得到一个更小的上界:
O
(
n
l
o
g
c
n
)
O(n^{log_cn})
O(nlogcn)
证明:如果一个border集合S存在长度为a,b,c(
a
⩽
b
⩽
c
a\leqslant b \leqslant c
a⩽b⩽c)的border并且
a
+
b
⩾
c
a+b\geqslant c
a+b⩾c
该border集合可以化简成成一个更小的border集合S’
把a,b,c变成
c
−
g
c
d
(
c
−
a
,
c
−
b
)
,
c
c-gcd(c-a,c-b),c
c−gcd(c−a,c−b),c即可。
不停将S变换直到不能变换。
注意到,如果两个border集合化简后的集合一样,那么这两个border集合相同。
即一个关键集合对应着一个border集合。
然后关键集合元素个数是
l
o
g
c
log_c
logc级别的,于是得证。
这个上界很松,因为关键集合有很多不合法状态。
其次,有可能一个border集合可以化简成多个关键集合。
在这道题里面,长度为M的border集合只有几千个。
于是对每个border集合分别dp。
令
f
i
f_i
fi表示对于当前border集合,第一次出现位置在
[
i
−
M
+
1
,
i
]
[i-M+1,i]
[i−M+1,i]的方案数。
则
f
i
=
A
i
−
M
−
∑
j
=
1
i
−
1
f
j
∗
w
a
y
(
j
,
i
)
f_i=A^{i-M}-\sum_{j=1}^{i-1}f_j*way(j,i)
fi=Ai−M−∑j=1i−1fj∗way(j,i)
当两个串不重叠时,中间部分任意。
重叠,检查是否为border即可。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int Mod=1000000007;
int n,m,A;
#define Maxn 205
#define V 100010
int power[Maxn];
bool vis[Maxn];
int cnt=0;
ll type[V];
int ans[V];
int fa[Maxn],val[Maxn];
int nxt[Maxn];
int getroot(int x){
if(fa[x]!=x)fa[x]=getroot(fa[x]);
return fa[x];
}
inline void merge(int x,int y){
int fx=getroot(x);int fy=getroot(y);
if(fx==fy)return;
fa[fx]=fy;
}
inline int calc(){
for(int i=1;i<=m;++i)fa[i]=i;
for(int i=1;i<m;++i)
if(type[cnt]&(1ll<<i))
for(int j=1;j<=i;++j)merge(j,m-i+j);
int res=1;
for(int i=1;i<=m;++i)
if(getroot(i)==i)res=1ll*res*A%Mod;
return res;
}
inline bool Judge(int maxv){
for(int i=1;i<=m;++i)fa[i]=i;
for(int i=1;i<=maxv;++i)
if(vis[i])
for(int j=1;j<=i;++j)merge(j,m-i+j);
for(int i=1;i<=m;++i)val[i]=getroot(i);
nxt[0]=-1;
for(int i=1;i<=m;++i){
int tmp=nxt[i-1];
while(tmp!=-1&&val[tmp+1]!=val[i])tmp=nxt[tmp];
tmp++;
nxt[i]=tmp;
}
int tmp=nxt[m];
ll sta=0;
while(tmp>0){
sta|=(1ll<<tmp);
tmp=nxt[tmp];
}
for(int i=1;i<=maxv;++i)
if(!vis[i]&&(sta&(1ll<<i)))return false;
return true;
}
void dfs(int u){
if(u==m){
cnt++;
type[cnt]=0;
for(int i=1;i<m;++i)
if(vis[i])type[cnt]|=(1ll<<i);
ans[cnt]=calc();
for(register int i=1;i<cnt;++i)
if((type[i]&type[cnt])==type[cnt])ans[cnt]=(ans[cnt]-ans[i]+Mod)%Mod;
return;
}
vis[u]=true;
if(Judge(u))dfs(u+1);
vis[u]=false;
if(Judge(u))dfs(u+1);
}
int Ans=0;
int dp[Maxn];
inline void solve(int x){
memset(dp,0,sizeof(dp));
for(int i=0;i<m;++i)dp[i]=0;
int res=0;
for(int i=m;i<=n;++i){
dp[i]=power[i-m];
for(int j=m;j<i;++j){
if(m+j-i>0&&(type[x]&(1ll<<(m+j-i))))dp[i]=(dp[i]-dp[j]+Mod)%Mod;
else if(m+j-i<=0)dp[i]=(dp[i]-1ll*power[i-j-m]*dp[j]%Mod+Mod)%Mod;
}
res=(res+1ll*dp[i]*power[n-i])%Mod;
}
Ans=(Ans+1ll*res*ans[x])%Mod;
}
int main(){
scanf("%d%d%d",&n,&m,&A);
power[0]=1;
for(register int i=1;i<=n;++i)power[i]=1ll*power[i-1]*A%Mod;
dfs(1);
for(register int i=1;i<=cnt;++i)solve(i);
printf("%d\n",Ans);
return 0;
}