五大经典算法之三动态递归DP

五大经典算法 动态递归DP

首先需要决定存储什么历史信息,以及用什么数据结构来存储。然后最重要的就是递推公式,最后需要考虑起始条件的值。

Leetcode 139. Word Break

要求一个非空字符串s,一个非空的字符串词典,判断s能够通过空格组成一个序列是词典里的多个单词:

例如s=”leetcode”

dict=[“leet”,”code”

]

因为“leetcode”可以改成“leet code”故返回1

使用DP,我们用dp[i]表示到字符串s的第i个元素为止能不能用字典里的词表示。假设已经知道dp[0,1,,,,i-1]的结果,要求dp[i],其递推关系:

len取自[minlen,maxlen],对于i从0到s.length,都有:

dp[i+len]=1 when dp[i]=1&&wordDict.fin(s.substr(i,len))

代码:


  
 
  1. //word break
  2. bool find_vector(vector<string> ss,string s){
  3. int i= 0;
  4. while(i<ss.size()){ if(s.compare(ss[i])== 0) return 1;i++;}
  5. return 0;
  6. }
  7. bool wordBreak(string s,vector<string>& wordDict){
  8. if(s.size()< 1) return 0;
  9. vector< bool> dp(s.length()+ 1, false);
  10. dp[ 0]= true;
  11. int min_len=INT_MAX,max_len= 0;
  12. for( int i= 0;i<wordDict.size();i++){ if(min_len>wordDict[i].size()){min_len=wordDict[i].size();}
  13. if(max_len<wordDict[i].size()){ cout<< 1;max_len=wordDict[i].size();}
  14. }
  15. for( int i= 0;i<s.size();i++){
  16. if(dp[i]){ for( int len=min_len;i+len<=s.size()&&len<=max_len;len++)
  17. if(find_vector(wordDict,s.substr(i,len)))dp[i+len]= 1;
  18. }
  19. if(dp[s.length()]) return 1;
  20. }
  21. return dp[s.length()];
  22. }

参考:Word Break

132. Palindrome Partitioning II
要求给定一个字符串,要求分成的若干字串都是回文,求最小分串次数。

本题采用动态规划法从后往前,引出dp数组,dp[i]表示s[0…i]的最小分割次数;

dp[i]初始化为i,每一个之间切一刀,这是最大值了;

若从0到i之间存在j,0<j<i,且有s[j…i]是回文,那么此时dp[i+1]=min(dp[i1+],dp[j]+1)

故递推公式:

dp[i+1]=min(dp[i1+],dp[j]+1)


  
 
  1. vector < vector < bool> > getdict( string s){
  2. vector< vector< bool> > res;
  3. for( int i= 0;i<s.length();i++){
  4. vector< bool> aa;
  5. for( int j= 0;j<s.length();j++){aa.push_back( 0);};
  6. res.push_back(aa);
  7. }
  8. if(s.size()< 1) return res;
  9. for( int i=s.length() -1;i>= 0;i--)
  10. for( int j=i;j<s.length();j++)
  11. if(s[i]==s[j]&&((j-i< 2)||res[i+ 1][j -1]))res[i][j]= 1;
  12. return res;
  13. } //判断任意字串之间是不是回文
  14. //palindrome Partitioning11
  15. int minCut(string s){
  16. int len= 0;
  17. if(s.length()< 1) return len;
  18. vector< vector< bool> >dict=getdict(s);
  19. vector< int> dp(s.length()+ 1, 0);
  20. dp[ 0]= 0;
  21. for( int i= 0;i<s.size();i++){
  22. {
  23. dp[i+ 1]=i+ 1; //初始化
  24. for( int j= 0;j<=i;j++)
  25. if(dict[j][i])
  26. dp[i+ 1]=min(dp[i+ 1],dp[j]+ 1);
  27. }
  28. }
  29. return dp[s.size()] -1;
  30. }

参考:132. Palindrome Partitioning II

44. Wildcard Matching

已知‘?’可以匹配任何一个字符

已知‘×’可以匹配0个或者多个任意字符

求两个字符串是否完全匹配。

类似上面的,这里引入动态规划,设dp[i][j]表示s的前i个字符与p的前j个字符的匹配情况,其递推公式:

dp[i][j]=dp[i][j-1]||dp[i-1,j] when p[j]==’*’,当此星号表示0个字符时,则主要看dp[i][j-1],当星号代表字符时,则结果主要在于前面dp[i-1][j]

dp[i][j]=dp[i-1][j-1]&&(s[i]==p[j]||p[j]==’?’) when p[j]!=’*’

注意s的前i个字符与p的前j个字符的匹配情况,每次i+1的结果只依赖与i 与j,在程序中可以设置二重循环,则此时二维数组可以降为一维

dp[i+1]=dp[i]&&(p[j]==’?’||p[j]==s[i]) when p[j]!=’*’

当p[j]==’*’时,在次情形下,可以匹配任何情况,即只要遍历dp,发现dp[i]=1,则之后的结果肯定都是1

代码如下:


  
 
  1. bool isMatch(string s,string p){
  2. if(s.size()< 1&&p.size()< 1) return 1;
  3. int slen=s.size(); int plen=p.size();
  4. int i= 0;
  5. int num= 0;
  6. while(i<plen){ if(p[i]== '*')num++;i++;}
  7. if(plen-num>slen) return 0;
  8. vector< bool> dp(slen+ 1, false);
  9. dp[ 0]= true;
  10. for( int j= 0;j<plen;j++){
  11. if(p[j]!= '*'){
  12. for(i=slen;i>= 0;i--) //以为后面更新了dp[0],故不能从i=0开始
  13. dp[i+ 1]=dp[i]&&(p[j]==s[i]||p[j]== '?'); //此处体现i依赖与j
  14. }
  15. else {
  16. i= 0;
  17. while(i<=slen&&!dp[i])i++;
  18. for(;i<=slen;i++)dp[i]= 1;
  19. }
  20. dp[ 0]=dp[ 0]&&(p[j]== '*'); //更新dp[0],因为dp[0]表示s是空串,只有是星才匹配
  21. }
  22. return dp[slen];
  23. }

摘自博文:五大经典算法之三动态递归DP

    原文作者:五大常用算法
    原文地址: https://blog.csdn.net/yangxingpa/article/details/81517293
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞