参考:经典动态规划:编辑距离 | labuladong 的算法笔记
解决两个字符串的动态规划问题,一般都是用两个指针 i, j
分别指向两个字符串的最后,然后一步步往前移动,缩小问题的规模。(从顶至下)
所有题目将根据递归,递归+备忘录,动态规划的方式书写代码
编辑距离
首先,我们定义指针 i ,j 指向两个字符串的最后
递归解法:
class Solution { public: int minDistance(string word1, string word2) { int len1=word1.size(); int len2=word2.size(); return dp(word1,len1-1,word2,len2-1); } int dp(string word1,int i,string word2,int j){ if(i==-1){return j+1;} if(j==-1){return i+1;} if(word1[i]==word2[j]){ return dp(word1,i-1,word2,j-1); } else{ //插入--将s1替换成s2,在word1的i位置插入,相当于插入的字母位于i+1 int a=dp(word1,i,word2,j-1)+1; //删除--将s1替换成s2,删除word1的i位置 int b=dp(word1,i-1,word2,j)+1; //替换--将s1替换成s2,替换word1的i位置 int c=dp(word1,i-1,word2,j-1)+1; return min(a,min(b,c)); } } };
递归+备忘录解法:
class Solution { public: vector<vector<int>> g; int minDistance(string word1, string word2) { int len1=word1.size(); int len2=word2.size(); g=vector<vector<int>>(510,vector<int>(510,-1)); return dp(word1,len1-1,word2,len2-1); } int dp(string word1,int i,string word2,int j){ if(i==-1){return j+1;} if(j==-1){return i+1;} if(g[i][j]!=-1){return g[i][j];} if(word1[i]==word2[j]){ g[i][j]=dp(word1,i-1,word2,j-1); } else{ g[i][j]=min(dp(word1,i,word2,j-1),min(dp(word1,i-1,word2,j),dp(word1,i-1,word2,j-1)))+1; } return g[i][j]; } };
动态规划解法:
这里将dp[i][j]定义为子串word1[0:i-1]和word2[0:j-1]之间的编辑距离。
class Solution { public: int minDistance(string word1, string word2) { int dp[510][510]={0};//字符串整体后移一位 int len1=word1.size(); int len2=word2.size(); /*数组初始化*/ for(int i=0;i<=len2;i++){ dp[0][i]=i; } for(int i=0;i<=len1;i++){ dp[i][0]=i; } /*dp*/ for(int i=1;i<=len1;i++){ for(int j=1;j<=len2;j++){ if(word1[i-1]==word2[j-1]){ dp[i][j]=dp[i-1][j-1]; } else{ dp[i][j]=min(dp[i][j-1],min(dp[i-1][j],dp[i-1][j-1]))+1; } } } return dp[len1][len2]; } };
最长递增子序列
递归解法:
class Solution { public: int lengthOfLIS(vector<int>& nums) { int len=nums.size(); int res=1; for(int i=0;i<len;i++){ res=max(res,length(nums,i)); } return res; } int length(vector<int>& nums,int i){ //cout<<"i:"<<i<<endl; int temp=1; for(int idx=0;idx<i;idx++){ if(nums[i]>nums[idx]){ //cout<<"idx:"<<idx<<endl; temp=max(temp,length(nums,idx)+1); } } return temp; } };
递归+备忘录解法(备忘录直接AC):
class Solution { public: int lengthOfLIS(vector<int>& nums) { int len=nums.size(); vector<int> g(2510,-1); int res=1; for(int i=0;i<len;i++){ res=max(res,length(nums,i,g)); } return res; } int length(vector<int>& nums,int i,vector<int>& g){ if(g[i]!=-1){return g[i];} int temp=1; for(int idx=0;idx<i;idx++){ if(nums[i]>nums[idx]){ temp=max(temp,length(nums,idx,g)+1); } } g[i]=temp; return temp; } };
动态规划解法:
这里将g[i]定义为
以nums[i]
这个数结尾的最长递增子序列的长度class Solution { public: int lengthOfLIS(vector<int>& nums) { vector<int> g(2510,1); int res=1; int len=nums.size(); for(int i=0;i<len;i++){ for(int j=0;j<i;j++){ if(nums[i]>nums[j]){ //cout<<"i:"<<i<<" j:"<<j<<endl; g[i]=max(g[i],g[j]+1); res=max(res,g[i]); } } } return res; } };
最大子数组和
整体的思路为:
maxSub(nums, j)/g[i]
有两种「选择」,要么与前面的相邻子数组连接,形成一个和更大的子数组;要么不与前面的子数组连接,自成一派,自己作为一个子数组。
递归解法(TLE,202/220):
class Solution { public: int maxSubArray(vector<int>& nums) { int len=nums.size(); long long int res=nums[0]; for(int i=0;i<len;i++){ res=max(res,maxSub(nums,i)); } return res; } long long int maxSub(vector<int>& nums,int j){ //以j为结尾的最大子数组和 if(j==-1){return 0;} long long int temp=nums[j]; return max(temp,maxSub(nums,j-1)+nums[j]); } };
递归+备忘录解法(备忘录直接AC):
class Solution { public: int maxSubArray(vector<int>& nums) { int len=nums.size(); long long int res=nums[0]; vector<long long int> g(100010,INT_MIN); for(int i=0;i<len;i++){ res=max(res,maxSub(nums,i,g)); } return res; } long long int maxSub(vector<int>& nums,int j,vector<long long int>& g){ //以j为结尾的最大子数组和 if(j==-1){return 0;} if(g[j]!=INT_MIN){return g[j];} long long int temp=nums[j]; g[j]=max(temp,maxSub(nums,j-1,g)+nums[j]); return g[j]; } };
动态规划解法:
这里将g[i]定义为
以nums[i]
这个数结尾的最大子数组和class Solution { public: int maxSubArray(vector<int>& nums) { int len=nums.size(); vector<int> g(100010,0); for(int i=0;i<len;i++){ g[i]=nums[i]; } int res=nums[0]; for(int i=1;i<len;i++){ g[i]=max(nums[i],g[i-1]+nums[i]); res=max(res,g[i]); } return res; } };
最长公共子序列
递归解法(TLE,202/220):
class Solution { public: int longest(string text1,string text2,int i,int j){ if(i==-1||j==-1){return 0;} int temp=0; if(text1[i]==text2[j]){ temp=max(temp,longest(text1,text2,i-1,j-1)+1); } else{ temp=max(temp,longest(text1,text2,i-1,j-1)); } temp=max(temp,longest(text1,text2,i,j-1)); temp=max(temp,longest(text1,text2,i-1,j)); return temp; } int longestCommonSubsequence(string text1, string text2) { int len1=text1.size(); int len2=text2.size(); int res=longest(text1,text2,len1-1,len2-1); return res; } };
递归+备忘录解法:
class Solution { public: int longest(string text1,string text2,int i,int j,vector<vector<int>>& g){ if(i==-1||j==-1){return 0;} if(g[i][j]!=-1){return g[i][j];} int temp=0; if(text1[i]==text2[j]){ temp=max(temp,longest(text1,text2,i-1,j-1,g)+1); } else{ temp=max(temp,longest(text1,text2,i-1,j-1,g)); } temp=max(temp,longest(text1,text2,i,j-1,g)); temp=max(temp,longest(text1,text2,i-1,j,g)); g[i][j]=temp; return temp; } int longestCommonSubsequence(string text1, string text2) { int len1=text1.size(); int len2=text2.size(); vector<vector<int>> g(1010,vector<int>(1010,-1)); int res=longest(text1,text2,len1-1,len2-1,g); return res; } };
动态规划解法:
这里将g[i][j]定义为子串text1[0:i]和text2[0:j]之间的最长公共子序列。
class Solution { public: int longestCommonSubsequence(string text1, string text2) { int len1=text1.size(); int len2=text2.size(); vector<vector<int>> g(1010,vector<int>(1010,-1)); for(int i=0;i<len1;i++){ for(int j=0;j<len2;j++){ if(text1[i]==text2[j]){ if(i-1>=0 && j-1>=0){ g[i][j]=max(g[i][j],g[i-1][j-1]+1); } else{g[i][j]=1;} } else{ if(i-1>=0 && j-1>=0){ g[i][j]=max(g[i][j],g[i-1][j-1]); } else{g[i][j]=0;} } if(j-1>=0){ g[i][j]=max(g[i][j],g[i][j-1]); } if(i-1>=0){ g[i][j]=max(g[i][j],g[i-1][j]); } } } return g[len1-1][len2-1]; } };