bzoj 3864 Hero meet devil
阿新 • • 發佈:2019-01-06
題意
一個脫氧核苷酸鏈只由AGCT組成,以下討論的串都只有AGCT
現在給出一個串S,問你對於i∈[0,|S|]有多少個長度為m的串T滿足LCS(S,T)=i
|S|<=15 m<=1000
題解
直觀的想法是dp套kmp?
反正下面講的是dp套dp
內層的dp是個顯然的LCS
盜個圖
我們觀察這個轉移式,可以發現只會用到兩行的資訊,而且同一行相鄰兩個之間相差不超過1
那就很好辦了,我們把一行的資訊差分,就能得到一個長度為m的01串,然後就可以把它狀壓
那麼
其中t(S,c)表示上一行是S,當前字元為c,所得到的狀態
這個可以預處理出來。預處理的具體方式就是先把狀態還原成字串,再進行一個單行的LCS,再進行壓縮
總的複雜度
怎麼看都不科學…但是能過…
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int L=1005,N=17,M=(1<<15)+5;
const int mod=1e9+7;
int n,m;
char s1[L];
int s[L];
int t[M][4];
int tot;
int p[2][N];
int d[L][M];
int cnt[M];
int ans[N];
int lowbit(int x){
return x&(-x);
}
void Init(){
for(int S=0;S<=tot;S++)
for(int c=0;c<4;c++){
memset(p,0,sizeof p);
for(int i=0;i<n;i++)
p[0][i+1]=p[0][i]+((S>>i)&1);
for(int i=1;i<=n;i++){
int tmp=0;
if(s[i]==c)
tmp=max(tmp,max(p[0][i],p[0][i-1]+1));
tmp=max(tmp,max(p[0][i],p[1][i-1]));
p[1][i]=tmp;
}
t[S][c]=0;
for(int i=0;i<n;i++)
t[S][c]+=(p[1][i+1]-p[1][i])<<i;
}
}
void dp(){
memset(d,0,sizeof d);
d[0][0]=1;
for(int i=1;i<=m;i++)
for(int S=0;S<=tot;S++)
for(int c=0;c<4;c++){
int T=t[S][c];
d[i][T]=(d[i][T]+d[i-1][S])%mod;
}
memset(ans,0,sizeof ans);
for(int S=0;S<=tot;S++)
ans[cnt[S]]=(ans[cnt[S]]+d[m][S])%mod;
}
int main()
{
for(int i=1;i<M;i++)
cnt[i]=cnt[i^lowbit(i)]+1;
int Kase;
scanf("%d",&Kase);
while(Kase--){
scanf("%s",s1+1);
n=strlen(s1+1);
tot=(1<<n)-1;
for(int i=1;i<=n;i++){
if(s1[i]=='A')
s[i]=0;
else if(s1[i]=='C')
s[i]=1;
else if(s1[i]=='G')
s[i]=2;
else
s[i]=3;
}
Init();
scanf("%d",&m);
dp();
for(int i=0;i<=n;i++)
printf("%d\n",ans[i]);
}
}