这道题会用到动态规划。题目的描述很简单,见标题。首先让我们明确两个概念:
最长公共子串(Longest Common Substring),子串是连续的;
最长公共子序列(Longest Common Subsequences), 不一定是连续的。
思路:
举个例子,abcdeca这个字符串的逆序为:acedcba. 这两者之间的公共最大子序列为:acdca. 再将其与原字符串相减就是需要删除的最少字符数。这样该问题实际上就是求字符与其反转的最长公共子序列!
下一步:如何求解两个字符串的最长公共子序列? 当然使用动态规划,用到循环的思路,比起递归时间性能好很多。
如何构建动态规划的函数关系
首先,第一步确定函数的参数和返回值,也就是函数的自变量和函数值,两个字符串可以想到自变量应该为2个,函数值只涉及到长度就姑且设为最长公共子序列的长度L。 一般来说,动态规划的起步都是从0开始的,也就是说我们可以先假设最简单的情况:如果是str1和str2是两个字符,那么L怎么判断。如果str1一个字符,str2两个字符又怎么判断,这样递推寻找函数关系式。
我们不妨设函数参数为m, n,分别表示str1和str2字符串的下标位置。 于是,求函数表达式 L(m, n)!
分析: 1. 如果str1(m) == str2(n), 则 L(m, n) = L(m-1, n-1) 2. 如果str1(m) != str2(n), 则 L(m, n) = max ( L(m, n-1), L(m-1, n) )
分析好了之后,接下来就是算法实现。在上述的递归函数关系中,肯定要确定好循环的初始条件
//
// Created by max on 19-4-26.
//
#include <iostream>
#include <cstring> //memset函数
#include <string> //string类
#include <cstdio> //C语言的输入输出
#include <algorithm> //用到max reverse
using namespace std;
const int MAXN = 1005;
int dp[MAXN][MAXN];
int main ()
{
cin.tie(0); //解除cin与cout的绑定 加快执行效率
ios::sync_with_stdio(false);
//C++中iostream为了兼容C,会用一个流缓冲区同步C的标准流,这一句可以使cin cout不经过缓冲区提高性能
string s;
memset(dp, 0, sizeof(dp));
while( cin >> s )
{
string t = s;
reverse(s.begin(), s.end());
int len = s.length();
int maxLen = 0; //保存最大长度
for( int i = 0; i < len; ++i)
{
if(t[0] == s[i]) //把t字符串下标看做函数L的第一个参数
while(i < len) dp[0][i++] = 1;
else
dp[0][i] = 0;
} //将动态规划的二维数组中的第一行全部求解了
for( int i = 0; i < len; i++)
{
if( t[i] == s[0])
while(i < len) dp[i++][0] = 1;
else
dp[i][0] = 0;
} //第一列
for(int i=1; i<len; i++)
{
for(int j=1; j<len; j++)
{
if(t[i] == s[j])
dp[i][j] = dp[i-1][j-1] + 1;
else
dp[i][j] = max( dp[i-1][j], dp[i][j-1]);
}
}
maxLen = dp[len-1][len-1];
cout << "numbers of char to be deleted:" << len - maxLen << endl;
}
return 0;
}
That is all
Friday 愉快!