0
点赞
收藏
分享

微信扫一扫

[JZOJ3747] Problem C


Description

基因串是由ACGT4个字母组成的,我们有一个长度为n的基因串St。想要知道长度为m的基因串中,与S的最长公共子序列分别为0; 1; ; n的串各有几个。
输出答案关于10^9 + 7的余数。

10%的数据n <= 3;m <= 10。
50%的数据n <= 6;m <= 100。
100%的数据n <= 10;m <= 1000。

Solution

一开始我想的是容斥,然而发现这是不行的。

n<=10,一般这样的只有几种情况,要么暴力,要么矩乘,要么容斥,要么状压。

仔细想想,似乎只有状压可行。
枚举每个位置选什么
设Ti表示原串中第i个位置结尾与构造出来的串的最长公共子序列长度
设fi,S表示当前枚举构造串到了第i个位置,把T压入S中

如果直接按照这样来转移S,S是1010。
优化一下
设gi表示max(Tj),1≤j≤i

每个最长公共子序列都是有前面的转移过来的,所以gi≤gi−1+1
也就是说,gi只有两种情况,gi−1,gi−1+1
那么状态只需要是这个位置g的增加量就行了,只有0和1

那么S就变成了210
每次转移将g还原出来,选的字母看能匹配哪些位置,直接转移修改。
复杂度O(m∗2n∗n∗4)

Code

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define mo 1000000007
#define LL long long
#define N 12
#define M 1005
using namespace std;
LL ans[N],f[M][1100];
int n,m,a[N],cf[N],g[N],g1[N];
int main()
{
char ch[N];
scanf("%s",ch+1);
n=strlen(ch+1);
cf[0]=1;
fo(i,1,n)
{
if(ch[i]=='A') a[i]=0;
if(ch[i]=='C') a[i]=1;
if(ch[i]=='G') a[i]=2;
if(ch[i]=='T') a[i]=3;
cf[i]=cf[i-1]*2;
}
memset(f,0,sizeof(f));
memset(ans,0,sizeof(ans));
cin>>m;
f[0][0]=1;
fo(i,1,m)
{
fo(j,0,cf[n]-1)
{
fo(p,0,n-1)
if((j&cf[p])>0) g[p+1]=g[p]+1;
else g[p+1]=g[p];
fo(k,0,3)
{
int s1=0;
g1[0]=0;
fo(p,0,n-1)
{
g1[p+1]=max(g1[p],g[p+1]+(a[p+1]==k&&(j&cf[p])==0));
s1+=(g1[p+1]-g1[p])*cf[p];
}
f[i][s1]=(f[i][s1]+f[i-1][j])%mo;
}
}
}
fo(j,0,cf[n]-1)
{
int v=0;
fo(p,0,n-1) if((cf[p]&j)>0) v++;
ans[v]=(ans[v]+f[m][j])%mo;
}
fo(i,0,n) printf("%lld\n",ans[i]);
}


举报

相关推荐

0 条评论