1288: [蓝桥杯2016初赛]密码脱落 C/C++

题目描述:

X星球的考古学家发现了一批古代留下来的密码。这些密码是由A、B、C、D 四种植物的种子串成的序列。
仔细分析发现,这些密码串当初应该是前后对称的(也就是我们说的镜像串)。
由于年代久远,其中许多种子脱落了,因而可能会失去镜像的特征。
你的任务是:给定一个现在看到的密码串,计算一下从当初的状态,它要至少脱落多少个种子,才可能会变成现在的样子。

输入格式:

输入存在多组测试数据,对于每组测试数据输入一行,表示现在看到的密码串(长度不大于1000)

输出格式:

对于每组测试数据要求输出一个正整数,表示至少脱落了多少个种子。

输入样例:

ABCBA
ABDCDCBABC

输出样例:

0
3

 仔细研究本题我们可以发现一个规律:

"ABCBA"的长度为5,逆序为"ABCBA",而他们的公共最长子序列为"ABCBA",长度为5。

因此至少脱落了5-5=0个种子。

"ABDCDCBABC"的长度为10,逆序为"CBABCDCDDBA",而他们的公共最长子序列为"ABCDCBA",长度为7。

因此至少脱落了10-7=3个种子。

所以本题归根结底就是一个求最长公共子序列(LCS)的题目。而求LCS我们可以使用动态规划(dp)的办法。

状态设置:

  • f[i][j]表示的是序列Si = {x1,x2,x3,...,xi}与序列Sj = {y1,y2,y3,...,yj}的最长公共子序列的长度。

状态转移方程:

  • 当i=0,j=0时,f [ i ][ j ] = 0;
  • 当xi = yi时,f [ i ][ j ] = 1 + f [ i - 1 ][ j - 1 ];
  • 当xi != yi时,f [ i ][ j ] = max( f [ i - 1 ][ j ] , f [ i ][ j - 1 ] );

最长子序列即为f [ n ][ m ](n为s1的长度,m为s2的长度)。

#include 
using namespace std;
int n;
int f[1001][1001];

int LCS(string s1,string s2)
{
	for(int i = 1;i <= n;i++)
	{
		for(int j = 1;j <= n;j++)
		{
			if(s1[i-1] == s2[j-1])
			{
				f[i][j] = 1 + f[i-1][j-1];
			}
			else
			{
				f[i][j] = max(f[i-1][j],f[i][j-1]);
			}
		}
	}
	return f[n][n];
}

int main()
{
	string s1;
	while(cin >> s1)
	{
		n = s1.length();
		string s2 = s1;
		for(int i = 0;i < n;i++)
		{
			s2[n-i-1] = s1[i];
		} 
		int ans = n-LCS(s1,s2);
		cout << ans << endl;
	} 
	
	return 0;
}

你可能感兴趣的:(蓝桥杯真题题解,数据结构与算法,蓝桥杯,c++,动态规划)