【题目链接】
https://loj.ac/problem/2575
【题解】
考虑状压最长公共子序列dp数组的一行(K个)。显然这个数组是单调增的,那么就可以用k个0/1位表示。再预处理出转移,就可以dp了。
时间复杂度
O(N∗2K∗3∗3)
O
(
N
∗
2
K
∗
3
∗
3
)
【代码】
# include <bits/stdc++.h>
# define ll long long
# define inf 0x3f3f3f3f
# define K 15
# define N 1010
using namespace std;
int read(){
int tmp = 0, fh = 1; char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') fh = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9'){tmp = tmp * 10 + ch - '0'; ch = getchar(); }
return tmp * fh;
}
const int P = 1e9 + 7;
char s[K + 1];
int num[K + 1], n, k, f[2][3][1 << K], cnt[K + 1], now[K + 1], to[1 << K][3], ans[K + 10];
inline void inc(int &x, int y){ x = (x + y) % P;}
int main(){
// freopen("C.in", "r", stdin);
// freopen(".out", "w", stdout);
n = read(), k = read();
scanf("\n%s", s + 1);
for (int i = 1; i <= k; i++){
if (s[i] == 'N') num[i] = 0;
if (s[i] == 'O') num[i] = 1;
if (s[i] == 'I') num[i] = 2;
}
for (int i = 0; i < (1 << k); i++){
for (int j = 1; j <= k; j++){
cnt[j] = cnt[j - 1];
if ((i & (1 << (j - 1))) != 0) cnt[j]++;
}
for (int j = 0; j < 3; j++){
for (int t = 1; t <= k; t++){
now[t] = max(now[t - 1], cnt[t]);
if (j == num[t]) now[t] = max(now[t], cnt[t - 1] + 1);
}
int tmp = 0;
for (int t = 1; t <= k; t++)
if (now[t] != now[t - 1]) tmp = tmp + (1 << (t - 1));
to[i][j] = tmp;
}
}
int f1 = 0, f2 = 1;
f[f1][0][0] = 1;
for (int i = 0; i < n; i++){
// t = 0 N -> 1 O -> 0 I -> 0
for (int j = 0; j < (1 << k); j++){
inc(f[f2][1][to[j][0]], f[f1][0][j]);
inc(f[f2][0][to[j][1]], f[f1][0][j]);
inc(f[f2][0][to[j][2]], f[f1][0][j]);
}
// t = 1 N -> 1 O -> 2 I -> 0
for (int j = 0; j < (1 << k); j++){
inc(f[f2][1][to[j][0]], f[f1][1][j]);
inc(f[f2][2][to[j][1]], f[f1][1][j]);
inc(f[f2][0][to[j][2]], f[f1][1][j]);
}
// t = 2 N -> 1 O -> 0 I -> inf
for (int j = 0; j < (1 << k); j++){
inc(f[f2][1][to[j][0]], f[f1][2][j]);
inc(f[f2][0][to[j][1]], f[f1][2][j]);
}
swap(f1, f2);
for (int j = 0; j < (1 << k); j++)
f[f2][0][j] = f[f2][1][j] = f[f2][2][j] = 0;
}
for (int i = 0; i < (1 << k); i++){
for (int j = 1; j <= k; j++){
cnt[j] = cnt[j - 1];
if ((i & (1 << (j - 1))) != 0) cnt[j]++;
}
ans[cnt[k]] = (1ll * ans[cnt[k]] + f[f1][0][i] + f[f1][1][i] + f[f1][2][i]) % P;
}
for (int i = 0; i <= k; i++)
printf("%d\n", ans[i]);
return 0;
}