LGP7152题解
考虑一件事情,一个序列操作一遍只可能对应唯一的一个结果序列,所以这是个双射。
进一步发现影响这个的只有分割线的位置,于是很容易想到以分割线为段进行 dp。
但是有一个问题,问号的位置并不好判定。
字符集只有 \(4\),于是设 \(dp[n][a][b]\) 表示有多少种序列操作后为 \([1,n]\) 的序列,且这一段的第一个元素为 \(a\),最后一个元素为 \(b\)。
正常情况会考虑对中间部分的转移列矩阵然后直接计算的,但是这样转移会涉及到矩阵点积做不了。
于是考虑把中间的部分也加到 dp 状态中,\(dp[n][a][b][c]\) 表示目前段最左边元素为 \(b\),最右边元素为 \(c\) 且上一段最左边元素为 \(a\)。(这里的段是在输入序列上的段)
每次决策只可能有一个字母,所以复杂度是 \(O(4^4n)\) 的。
#include<cstring>
#include<cstdio>
#include<cctype>
namespace SOLVE{
const int M=1e5+5,mod=1e9+7;
int n,s[M],dp[M][4][4][4];char c[M];
inline int get(const char&s){
return s=='A'?0:s=='C'?1:s=='T'?2:s=='G'?3:-1;
}
inline void main(){
scanf("%s",c+1);n=strlen(c+1);for(int i=1;i<=n;++i)s[i]=get(c[i]);
for(int x=0;x^4;++x)for(int y=0;y^4;++y)dp[1][y][x][x]=!~s[1]||s[1]==x;
for(int i=2;i<=n;++i){
for(int x=0;x^4;++x){
if(~s[i]&&s[i]!=x)continue;
for(int a=0;a^4;++a)for(int b=0;b^4;++b)for(int c=0;c^4;++c){
if(x!=c)dp[i][a][b][x]=(dp[i][a][b][x]+dp[i-1][a][b][c])%mod;
if(a==c)dp[i][b][x][x]=(dp[i][b][x][x]+dp[i-1][a][b][c])%mod;
}
}
}
int ans(0);for(int x=0;x^4;++x)for(int y=0;y^4;++y)ans=(ans+dp[n][x][y][x])%mod;printf("%d",ans);
}
}
signed main(){
SOLVE::main();
}