题目描述
有两个串,一个模式串,长度为K,一个匹配串(需要自己去凑),长度N,满足以下条件:
1.两个串只含“N”、“O”、“I”三种字符
2.匹配串不能出现子串“NOI”
对于每种匹配串和模式串的最长公共子序列长度 假设为s,求每个s对应多少种不同的匹配串
题解
首先我们可以很容易想到一个dp方程,对于匹配串匹配到第i位,对应模式串现在的匹配情况为j,而且NOI在末尾出现的情况为0/1/2
至于对于模式串的转移,其实是有技巧的,假设模式串当前状态是psta,后面加一个字符j,我们不可以直接在后面找一个字符j,因为有可能虽然后面没有这个字符,但是以后也会用到。
比如模式串为:NOO,然后当前状态为6,就是选了后面的OO,然后现在要加个N
其实如果不选N,状态还是OO,那么以后的状态可能会出现问题。
如果选了N,转移出来的状态会是什么样子呢?
答案是转移到5,就是选第一个N和第三个O。(不能选第二个,因为以后可能再来一个O)
所以应该怎么转移呢,其实就是一个LCS:
rep(p,0,k-1) h[p] = g[p] = 0;
rep(p,0,k-1) if(i&(1ll<<p)) g[p] = 1; else g[p] = 0;
rep(p,0,k-1) g[p] = (g[p-1] + g[p]) % mod;
rep(p,0,k-1) h[p] = max(h[p-1] , max(g[p] , (g[p-1] + 1) * (s[p+1] == str[j])) );
rep(p,0,k-1) if(h[p] - h[p-1]) psta[i][j] |= (1ll << p);
大概的思想就是,对于状态的第i位数,与模式串1-i的最长公共子序列就是前i个数中1的数量,然后要是这样LCS再差分,就会使1都在前面尽量出现。
代码
#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define dep(i,a,b) for(int i=(a);i>=(b);--i)
#define fi first
#define se second
#define MP make_pair
#define PB push_back
#define int long long
const int N = 5e6+10;
const int mod = 1e9 + 7;
using namespace std;
inline int rd() {
int p=0; int f=1; char ch=getchar();
while(ch<'0' || ch>'9'){if(ch=='-') f*=-1; ch=getchar();}
while(ch>='0' && ch<='9'){p=p*10+ch-'0'; ch=getchar();}
return p*f;
}
char str[4]={"NOI"};
char s[N]; int f[2][33000][4]; int psta[33000][4];
void add(int &x,int y){x=(x+y)%mod;}
int ans[16]; int g[16],h[16];
signed main() {
freopen("a.in","r",stdin);
int n = rd(); int k = rd();
rep(i,1,k) scanf("\n%c",&s[i]);
int mx = (1ll << k) - 1;
rep(i,0,mx) {
rep(j,0,2) {
rep(p,0,k-1) h[p] = g[p] = 0;
rep(p,0,k-1) if(i&(1ll<<p)) g[p] = 1; else g[p] = 0;
rep(p,0,k-1) g[p] = (g[p-1] + g[p]) % mod;
rep(p,0,k-1) h[p] = max(h[p-1] , max(g[p] , (g[p-1] + 1) * (s[p+1] == str[j])) );
rep(p,0,k-1) if(h[p] - h[p-1]) psta[i][j] |= (1ll << p);
// printf("%lld %lld : %lld\n",i,j,psta[i][j]);
}
}
int op = 0; f[0][0][0] = 1;
rep(i,1,n) {
memset(f[op^1],0,sizeof(f[op^1]));
rep(j,0,mx) {
rep(k,0,2) // 前k个相同
rep(p,0,2) {
if(p == k) {
if(k!=2) add(f[op^1][psta[j][p]][k+1] , f[op][j][k]);
}
else {
if(p==0) add(f[op^1][psta[j][p]][1] , f[op][j][k]);
else add(f[op^1][psta[j][p]][0] , f[op][j][k]);
}
}
}op ^= 1;
}
rep(i,0,mx) rep(j,0,2) add(ans[__builtin_popcount(i)],f[op][i][j]);
rep(i,0,k) printf("%lld\n",ans[i]);
return 0;
}