problem
给定一个模式串
S
S
S,长度为
k
k
k,只含字符 'N','O','I'
。对于
∀
i
∈
[
0
,
k
]
\forall i\in[0,k]
∀i∈[0,k],求出满足以下条件的串的数量:
- 长度为
n
n
n 且只含字符
'N','O','I'
。 - 与 S S S 的最长公共子序列长度恰好为 i i i。
- 不含子串
NOI
。
答案对 1 0 9 + 7 10^9+7 109+7 取模。
数据范围: n ≤ 1000 n≤1000 n≤1000, k ≤ 15 k≤15 k≤15。
solution
参考博客。
首先考虑一下最长上升子序列怎么求,设 d p i , j dp_{i,j} dpi,j 表示 A A A 串前 i i i 个字符, B B B 串前 j j j 个字符的答案,则:
l c s i , j = m a x { l c s i − 1 , j , l c s i , j − 1 , l c s i − 1 , j − 1 + [ A i = B j ] } \mathrm{lcs}_{i,j}=max\{\mathrm{lcs}_{i-1,j},\mathrm{lcs}_{i,j-1},\mathrm{lcs}_{i-1,j-1}+[A_i=B_j]\} lcsi,j=max{lcsi−1,j,lcsi,j−1,lcsi−1,j−1+[Ai=Bj]}
设 B B B 串就是题中的模式串 S S S,是已知的。也就是说,我们要求 l c s i \mathrm{lcs}_i lcsi 这一行,只需要 l c s i − 1 \mathrm{lcs}_{i-1} lcsi−1 这一行和 A i A_i Ai。
发现 l c s i , j − l c s i , j − 1 ≤ 1 \mathrm{lcs}_{i,j}-\mathrm{lcs}_{i,j-1}\le 1 lcsi,j−lcsi,j−1≤1,那么对 l c s i \mathrm{lcs}_i lcsi 差分一下会是一个长度为 k k k 的 01 01 01 串,是可以状态压缩的!
那么我们可以再设计一个
d
p
dp
dp:
f
i
,
j
,
0
/
1
/
2
f_{i,j,0/1/2}
fi,j,0/1/2 表示
d
p
dp
dp 到第
i
i
i 位,
l
c
s
i
\mathrm{lcs}_i
lcsi 的状态为
j
j
j,第
i
i
i 位填 'N'
/
/
/'O'
/
/
/'I'
的方案数。那么,我们可以把
l
i
s
i
\mathrm{lis}_i
lisi 还原成数组,按照
l
i
s
\mathrm{lis}
lis 的
d
p
dp
dp 转移后得到新的状态,再对填 'N'
/
/
/'O'
/
/
/'I'
简单分类讨论一下得到转移方程。
注意在统计答案的时候,若状态 i i i 中有 n u m num num 个 1 1 1,那么 i i i 的贡献应该加到 n u m num num 里(因为 i i i 是经过差分后再状压的)。
时间复杂度 O ( n k 2 k ) O(nk2^k) O(nk2k)。
code
#include<bits/stdc++.h>
using namespace std;
const int N=(1<<15)+5,P=1e9+7;
int n,k,f[2][N][3]={1},bit[N],ans[1005];
void Add(int &x,int y) {x=(x+y>=P)?x+y-P:x+y;}
int A[20],B[20];
void Get_array(int sta){
for(int i=0;i<k;++i) A[i+1]=(sta>>i)&1;
for(int i=1;i<=k;++i) A[i]+=A[i-1];
}
int Get_sta(){
int sta=0;
for(int i=0;i<k;++i) sta|=((B[i+1]-B[i])<<i);
return sta;
}
char S[20];
void dp(int t,int j,int num,char c,int val){
Get_array(j);
for(int i=1;i<=k;++i) B[i]=max(max(A[i],B[i-1]),A[i-1]+(c==S[i]));
int x=Get_sta();
Add(f[t][x][num],val);
}
int main(){
scanf("%d%d%s",&n,&k,S+1);
int sta=(1<<k),t=1;
for(int i=1;i<=n;++i,t^=1){
memset(f[t],0,sizeof(f[t]));
for(int j=0;j<sta;++j){
if(f[t^1][j][0]){
dp(t,j,1,'N',f[t^1][j][0]),
dp(t,j,0,'O',f[t^1][j][0]),
dp(t,j,0,'I',f[t^1][j][0]);
}
if(f[t^1][j][1]){
dp(t,j,1,'N',f[t^1][j][1]),
dp(t,j,2,'O',f[t^1][j][1]),
dp(t,j,0,'I',f[t^1][j][1]);
}
if(f[t^1][j][2]){
dp(t,j,1,'N',f[t^1][j][2]),
dp(t,j,0,'O',f[t^1][j][2]);
}
}
}
for(int i=0;i<sta;++i) bit[i]=bit[i>>1]+(i&1);
for(int i=0;i<sta;++i)
for(int j=0;j<3;++j) Add(ans[bit[i]],f[t^1][i][j]);
for(int i=0;i<=k;++i) printf("%d\n",ans[i]);
return 0;
}