显然,在遇到两个字符串需按位进行操作的问题时,我们首先会想到将两个字符串按位遍历;
类如:
int dp[][]; // for(int i=1;i<=a.size();i++) { for(int j=1;j<=b.size();j++) { if(a[i]==b[i]) {} else{} } }
dp[i][j]的i和j分别代表每个字符串遍历到了第几个字母;
那么,作为一个dp初学者,我们不妨照葫芦画瓢,对本题的s、t字符串进行按位遍历(以下我设a、b)字符串。
#include <bits/stdc++.h> using namespace std; #define int long long #define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); string a,b; int dp[5005][5005]; int ans=0; signed main() { cin>>a>>b; int len1=a.size(); int len2=b.size();以上为我的代码准备;
这个时候,我们进行可以很简单地想出来的第一步!
for(int i=1;i<=len1;i++) { for(int j=1;j<=len2;j++) { if(a[i-1]==b[j-1]) { dp[i][j]=dp[i-1][j-1]+1; }显然,我们可以知道,如果a[i]==b[j],那么ans是会++的;
而我们又知道!dp的特质是新的子问题的解,是由旧的子问题的解得来的!
那么我们很容易就可以得到,dp[i][j]=dp[i-1][j-1];
好的,问题来了,我们知道新解是由旧解得来的,我们又如何确定“旧解”是什么呢?
因为数据范围可以允许进行n^2的复杂度,并且需要遍历两个字符串;
并且!我们易知dp[i][j]一般是由dp[i-1][j]/dp[i][j-1]/dp[i-1][j-1]得到的;
所以!易得结果!
//第二步
else if(a[i-1]!=b[j-1]) { dp[i][j]=max(dp[i-1][j],dp[i][j-1]); }我个人认为比较有难度的是这个。
首先,我们秉持着这个——>dp的本质是继承/选择;
那么当a[i]!=b[j]时,继承什么?又选择什么呢?
继承:
显然我们要从dp[i-1][j]和dp[i][j-1]中继承;
再补一点原因:
(1)因为a[i]!=b[j],所以本质上这一步是没有即时性的操作的;所以一定是要从前面的旧解走的;
(2)显然dp[i-1][j-1]已经用过了,我们可以往dp[i-1][j]和dp[i][j-1]处想;
选择:
(1)显然是选择最大的;
(2)二维和“两个字符串的对称性”;
好的;现在我们就写出了该dp的状态转移方程。
#include <bits/stdc++.h> using namespace std; #define int long long #define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); string a,b; int dp[5005][5005]; signed main() { while(cin>>a>>b) { int len1=a.size(); int len2=b.size(); dp[0][0]=0; if(a[0]==b[0]) { dp[1][1]=1; } for(int i=1;i<=len1;i++) { for(int j=1;j<=len2;j++) { if(a[i-1]==b[j-1]) { dp[i][j]=dp[i-1][j-1]+1; } else if(a[i-1]!=b[j-1]) { dp[i][j]=max(dp[i-1][j],dp[i][j-1]); } } } cout<<dp[len1][len2]<<endl; } return 0; }