[TJOI2018] 游园会
一、题目
二、解法
考虑字符串计数 \(dp\) 的常见模型,设 \(dp(i,...,k)\) 表示已经填入了 \(i\) 个字符,现在串已经匹配到了 \(\tt NOI\) 长度为 \(k\) 的前缀,那么我们还需要把最长公共子序列记录到状态里面。
考虑最长公共子序列的求法是普通 \(dp\),设 \(f(i,j)\) 表示考虑 \(A\) 串长度为 \(i\) 的前缀和 \(B\) 串长度为 \(j\) 的前缀,它们的最长公共子序列长度,这里我们让 \(A\) 串是我们正在处理的串:
\[f(i,j)=\max\{f(i-1,j),f(i,j-1),f(i-1,j-1)+(A_i=B_j)\}
\]
考虑用一个自动机来表示这个 \(f\),自动机上的每一个节点都对应一个 \(f\),然后我们把原来的状态记成 \(dp(i,j,k)\) 表示匹配到了自动机上的第 \(j\) 个节点,这和 \(\tt AC\) 自动机、后缀自动机上 \(dp\) 类似,只不过自己设计自动机要更加灵活。
可以考虑记 \(f\) 的差分数组,这样自动机上的节点数一共只有 \(2^{15}\) 种,外层 \(dp\) 转移枚举填入 NOI
的哪一种字符,内层 \(dp\) 转移直接由上一层推出下一层即可,时间复杂度 \(O(nk\cdot 2^k)\)
这种用另一个 \(dp\) 检验合法,设计自动机记录状态的方法,我们称之为 \(dp\) 套 \(dp\)
#include <cstdio>
#include <iostream>
using namespace std;
const int M = 20;
const int N = 32780;
const int MOD = 1e9+7;
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int n,m,a[M],b[M],ans[M],siz[N],dp[2][N][3];char s[M];
int encode(int *a)
{
int r=0;
for(int i=0;i<m;i++)
r|=(a[i+1]-a[i])<<i;
return r;
}
void decode(int *a,int r)
{
for(int i=0;i<m;i++) a[i+1]=(r>>i)&1;
for(int i=1;i<=m;i++) a[i]+=a[i-1];
}
void trans(int w,int r,int p,char c,int v)
{
decode(a,r);
for(int i=1;i<=m;i++)
b[i]=max(max(a[i],b[i-1]),a[i-1]+(c==s[i]));
int tr=encode(b);
dp[w][tr][p]=(dp[w][tr][p]+v)%MOD;
}
signed main()
{
n=read();m=read();
scanf("%s",s+1);dp[0][0][0]=1;
for(int i=1;i<(1<<15);i++) siz[i]=siz[i>>1]+(i&1);
for(int i=0;i<n;i++)
{
int w=(i&1),tw=w^1;
for(int j=0;j<(1<<m);j++)
for(int p=0;p<3;p++)
dp[tw][j][p]=0;
for(int j=0;j<(1<<m);j++)
{
if(dp[w][j][0])
{
trans(tw,j,1,'N',dp[w][j][0]);
trans(tw,j,0,'O',dp[w][j][0]);
trans(tw,j,0,'I',dp[w][j][0]);
}
if(dp[w][j][1])
{
trans(tw,j,1,'N',dp[w][j][1]);
trans(tw,j,2,'O',dp[w][j][1]);
trans(tw,j,0,'I',dp[w][j][1]);
}
if(dp[w][j][2])
{
trans(tw,j,1,'N',dp[w][j][2]);
trans(tw,j,0,'O',dp[w][j][2]);
}
}
}
for(int i=0;i<(1<<m);i++)
for(int p=0;p<3;p++)
ans[siz[i]]=(ans[siz[i]]+dp[n&1][i][p])%MOD;
for(int i=0;i<=m;i++)
printf("%d\n",ans[i]);
}