【luogu P7152】Bovine Genetics G(DP)(字符串)

168 篇文章 0 订阅
91 篇文章 0 订阅

Bovine Genetics G

题目链接:luogu P7152

题目大意

定义把一个字符串操作时在所有连续相同位置切开字符串,把每一段反转再接上。
然后给你一个操作后的序列,有一些位置不知道,问你原来的字符串有多少可能。

思路

首先不难发现,对于一个字符串,你用不同的方式把它分段,得出来的原始串也会不一样。

那这让我们考虑用 DP 来做。
但是我们要想怎么分段才是合法的。
首先,一段内肯定不能有连续相同的。
接着,它翻转之后要两个旁边是相同的,那就是说,一个串的最后一个字符要等于上一个串的第一个字符。
不难想到有关的就只有上一个串的第一个字符,最后一个字符,你现在的字符。
(然后为了能够转移第一个字符,你还需要这个字符串的第一个字符)

那现在的字符不用枚举(如果是问号就都枚举一下),剩下的你就都枚举一下,反正 4 3 4^3 43 (有问号就是 4 4 4^4 44)无伤大雅。
然后就是设 f i , j , k , l f_{i,j,k,l} fi,j,k,l 为前 i i i 个搞好,上一段第一个是 l l l,这一段第一个是 j j j,最后一个是 k k k 的方案数。
然后你考虑现在一个字符 n o w now now,怎么可以开新段,怎么可以加进这一段。
那如果两个不相同,就可以加进这一段,就是 n o w ≠ k now\neq k now=k,那就可以 f i , j , n o w , l + = f i − 1 , j , k , l f_{i,j,now,l}+=f_{i-1,j,k,l} fi,j,now,l+=fi1,j,k,l
那如果之前的最后一个等于上一段第一个( k = l k=l k=l),就可以开新段,就是 f i , n o w , n o w , j + = f i − 1 , j , k , l f_{i,now,now,j}+=f_{i-1,j,k,l} fi,now,now,j+=fi1,j,k,l

然后初始状态就拿第一个字符去搞,然后从第二个字符开始 DP。
最后记得也要 k = l k=l k=l 才可以统计入答案。
记得取模。

代码

#include<cstdio>
#include<cstring>
#define ll long long
#define mo 1000000007

using namespace std;

char c[100001];
ll f[100001][4][4][4], ans;
int n;

int get_bh(char x) {
	if (x == 'A') return 0;
	if (x == 'C') return 1;
	if (x == 'G') return 2;
	return 3;
}

int main() {
	scanf("%s", c + 1);
	n = strlen(c + 1);
	
	if (c[1] == '?') {//先根据第一个搞预先状态
		for (int i = 0; i < 4; i++)
			for (int j = 0; j < 4; j++)
				f[1][i][i][j] = 1;
	}
	else {
		for (int j = 0; j < 4; j++)
			f[1][get_bh(c[1])][get_bh(c[1])][j] = 1;
	}
	
	for (int i = 2; i <= n; i++)//转移
		for (int j = 0; j < 4; j++)
			for (int k = 0; k < 4; k++)
				for (int l = 0; l < 4; l++) {
					int now = get_bh(c[i]);
					if (c[i] == '?') {
						for (now = 0; now < 4; now++) {
							if (now != k) {
								f[i][j][now][l] = (f[i][j][now][l] + f[i - 1][j][k][l]) % mo;
							}
							if (k == l) {
								f[i][now][now][j] = (f[i][now][now][j] + f[i - 1][j][k][l]) % mo;
							}
						}
					}
					else {
						if (now != k) {
							f[i][j][now][l] = (f[i][j][now][l] + f[i - 1][j][k][l]) % mo;
						}
						if (k == l) {
							f[i][now][now][j] = (f[i][now][now][j] + f[i - 1][j][k][l]) % mo;
						}
					}
				}
	
	for (int j = 0; j < 4; j++)//统计答案
		for (int k = 0; k < 4; k++)
			ans = (ans + f[n][j][k][k]) % mo;//记得也要遵守规则
	printf("%lld", ans);
	
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值