【题解】P5484 [JLOI2011] 基因补全

我的第一篇题解

题意很明了,求对 $ T $ 的一种补全方案 (添加字符) 使其与 $ S $ 配对 ( A - T / T - A / C - G / G - C )

首先将 $ T $已有的字符逆过来(即变为配对所需的字符),就可以发现 实际是求 $ S $ 中和 $ T $ 相同的子串数量

于是这道题就做完了做法很明了...

记 $ f_i $ 为以 $ T $ 中第 $ i $ 个字符结尾的字串数量 (此时字符下标从 1 开始计算)

将 $ S $ 从前往后扫一遍,更新数组。

那么具体如何更新呢?

我们知道,当我们在 $ S $ 的第 $ i $ 位中扫到 $ T $ 中的第 $ j $ 个字符时,它能与所有先前在 $ S $ 中以 $ T $ 中第 $ j-1 $ 个字符结尾的子串构成合法子串 ,于是据此得到 DP 方程:

$ f_i = f_i + f_{i-1} (S_i = T_j)$

$ f_m $为最终所求(根据题意, $ m $ 为字符串 $ T $ 的长度)。

注意:

  • 运算中数据很大,要开高精度
  • DP 数组要从后往前扫,否则会因为在同一位上不合法的重复更新 (即对 $ S $ 中同一个字符的反复使用更新 DP 数组) 而 WA (可以类比为 0-1 背包和完全背包之间的区别)。
  • 在处理 $ T $ 时注意看清代码,不要出现将一个字符变为其对应的字符后又还原的情况(否则会因为变回原字符转换字符无效卡 BUG)。

代码如下:

#include<bits/stdc++.h>
using namespace std;
const int N = 2002;
int n,m,ans[N][N],len[N];
char a[N],b[N];
int main()
{
	cin>>n>>m;
	for(int i = 1;i <= n;i++)
		cin>>a[i];
	for(int i = 1;i <= m;i++)
	{
		cin>>b[i];
		if(b[i] == 'A') b[i] = 'T';
		else if(b[i] == 'T') b[i] = 'A';
		else if(b[i] == 'C') b[i] = 'G';
		else if(b[i] == 'G') b[i] = 'C';
	}
	for(int i = 1;i <= m;i++) len[i] = 1;
	for(int i = 1;i <= n;i++)
		for(int j = m;j >= 1;j--)
		{
			if(a[i] == b[j])
			{
				if(j == 1)
				{
					ans[1][1]++;
					int pos = 1;
					while(ans[1][pos] >= 10)
					{
						ans[1][pos+1]++;
						ans[1][pos] %= 10;
						pos++;
						len[1] = max(len[1],pos);
					}
				}
				else
				{
					int maxlen = max(len[j],len[j-1]);
					for(int k = 1;k <= maxlen;k++)
						ans[j][k] += ans[j-1][k];
					for(int k = 1;k <= maxlen;k++)
						if(ans[j][k] >= 10)
						{
							ans[j][k+1]++;
							ans[j][k] %= 10;
						}
					len[j] = maxlen;
					if(ans[j][maxlen+1]) len[j] = maxlen+1;
				}
			}
		}
	for(int i = len[m];i >= 1;i--)
		cout<<ans[m][i];
	return 0;
}

后记:

Update:

2021.12.17: 更新了 LaTeX 和内容。

2021.12.18: 更新内容。

2021.12.18: 更新LaTeX。

posted @ 2022-01-31 23:58  Daniel2020  阅读(39)  评论(0)    收藏  举报