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();
}
posted @ 2022-09-13 14:48  Prean  阅读(12)  评论(0编辑  收藏  举报
var canShowAdsense=function(){return !!0};