汇总LeeCode前200题中所有涉及动态规划的算法题,用自己的逻辑整理此类问题的优化思路。

动态规划

    • 概述
    • 最长回文子串
    • 字符串匹配问题
    • 最长有效括号
    • 编辑距离
    • 交错字符串
    • 最大子序和
    • 不同路径问题
    • 最小路径和
    • 扰乱字符串
    • 解码方法
    • 不同的二叉搜索树
    • 不同的子序列

概述

新手上路,详细记录了下刷LeeCode动态规划专题的相关题目,文笔有限,一方面分享,另一方面也总结给自己复习,如果错误的地方,欢迎大神批评指证。

那么正式开始,动态规划的题型,最主要的步骤就是如何快速的构造记忆dp数组,但是往往dp数组的建议并不是特别容易,需要我们理解所记录的状态的具体含义,会给接替带来较大的难度。

面对动态规划的题,我一般尝试用暴力递归的方式去进行尝试,然后在暴力递归的基础上用动态规划思想去优化,往往解题变得简单明了。

最长回文子串

LeeCode5. 最长回文子串
最长回文子串
这题是leeCode非常经典的一道题,他的最优解法是Manacher算法,可以实现O(N)的平均时间复杂度。这里要呈现此题的动态规划解答,不对最优的解法详细阐述,后面仅提供一下Manacher的代码。

  • 首先需要找出字符串中的最长回文子串,也就是返回字符串从0 - n (字符串长度)之间的最长回文子串,暴力递归尝试如下。

  • 情况一: 如果当前字符串的头尾字符相等,那么有可能整体都是回文字符,进入下一层判别条件。子情况一: 头尾各缩进一位的子字符串如果是回文串,那么整体的s就是最长的回文子串。子情况二: 头尾各缩进一位的字符串如果不是回文串,那么整体的s肯定不是回文串,将此处的情况当成下面的情况考虑。

  • 情况二: 如果当前的字符串头尾字符不相等 ,那么当前的字符串肯定不是回文字符。所以当前字符串的最大回文子串可能出自两个地方。左边缩进一个字符后的字符串的最大回文字符串或者右边缩进一个字符后的字符串的最大回文字符串。

  • 好了,经过场面的三个步骤,暴力递归的思想就出来了,那么递归还要考虑结束条件。

  • 结束条件:当缩进步骤发生后,如果成了只有一个字符的字符串,那么肯定是回文串,返回即可。

  • 暴力版的暴力递归代码如下:

    // 最长回文子串的暴力递归解法public String longestPalindrome(String s) {// 直接暴力一点返回结果return getRes(s,0,s.length() - 1);}// 真正的技术在这里private String getRes(String s, int l, int r) {// 两个边界条件首先放上if(l == r) return s.charAt(l) + "";if(l > r) return  "";// 分析出来的情况一(如果两头都缩进后的最大回文子串长度不等于缩进后字符串,那么子字符串肯定也不是回文字符串)if (s.charAt(l) == s.charAt(r) && getRes(s,l+1,r-1).length() == r - l - 1) {return s.substring(l,r+1);}else{// 分析对应的情况erString s1 = getRes(s,l+1,r);String s2 = getRes(s,l,r-1);String ans = s1;// 返回较长的结果ans = ans.length() < s2.length() ? s2 : ans;return ans;}}
  • 走到这里基本思想已经有了,但是这个代码在LeeCode上面AC了39个案列就报了超时。那么下面就是要进行优化了,这里也就正式表现出了动态规划的强大之处,也可以更直观的理解暴力递归的操作逻辑。
  • 观察暴力递归的代码,我们可以发现当前的字符串的最大回文子串的求解,是在子问题的基础上分析得出来的,那么现在我们将子问题罗列出来:
  • 求缩进字符串左右各一个字符后的字符串的最大回文子串(问题一样,只是求解的对象变了,这就是递归的本质)
  • 求左边缩进一个字符后的字符串的最大回文子串。
  • 求左边缩进一个字符后的字符串的最大回文子串。
  • 在暴力递归中我们是不断的递归下去,从大问题深入到子问题,一路走到底,然后计算再一路回传,那么这其中就回带来大量的重复计算。举一个例子: 求解s[1 - 4]位置时可能会计算s[ 2 - 4 ]位置的最大回文字符串,然而当求解s[2 - 5]的字符串时,又有可能要重新计算了一遍s[2 - 4]位置的字符串。这个就是严重的重复计算。
  • 有了上面例子的解释,那么动态规划的优化工作之一就可以看出来了,存储重复计算的结果,我们用户一张表记录下s[2 - 4]的结果就可以了,当然这里如果用HashMap存储中间结果也是可以的,时间复杂会也会降低,但是效率还是没有动态规划好。
  • 回到题目,起始我们就可以看出几个简单的结果, 比如当l == r , r > r的结果我门可以直观的看出来,那么我们动态规划的思想就是从这里出发去推导大的问题。
  • 这里直接对着暴力递归的代码直接来进行动态规划,我们将暴力递归中的l指针,看作是二维数组的行数,r指针看作是数组的列数,那么一张边长为s长度的二维数组就创建出来了,存储的结果就是暴力递归中返回的结果,不难看出,如果要求某个格子的最大回文子串的时候,我们只需要他左边格子的信息,下面格子的信息,以及左下角格子的信息。那么下面的工作就是把这么一张二维数组中的每一个位置都填上元素就可以了。
  • 很显然,在遍历数组填元素之前,有一半以上的格子我们可以明确的知道它的值,比如 当l > r时,此格子填空字符串,l == r 时,此位置填上单个字符串。然后根据暴力递归的判断条件填格子。
  • 填格子的代码如下:
    // 暴力递归改动态规划public String longestPalindrome2(String s){if(s.length() < 2) return s;int n = s.length();String[][] dp = new String[n][n];for (int i = n - 1; i >= 0; i--) {for (int j = 0; j < n; j++) {if(i > j){dp[i][j] = "";}else if(i == j) {dp[i][j] = s.charAt(i) + "";}else if(s.charAt(i) == s.charAt(j) && dp[i+1][j-1].length() == j - i - 1){dp[i][j] = s.substring(i,j+1);}else{// 找出三个之中最长的String s1 = dp[i+1][j];    // 左边缩进一位的结果String s2 = dp[i][j - 1];  // 右边缩进一位的结果String cur = s1;cur = cur.length() < s2.length() ? s2 : cur;dp[i][j] = cur;}}}return dp[0][n - 1];}
  • 这里就是动态规划的雏形了,很可惜leeCode上运行还是超时,不过AC过了119个案例,大大提升了性能。好了,那么后面就是通用的二维表格优化了。分析一下时间都浪费在哪里,整体代码遍历二维数组肯定没问题,那么问题肯定出在了计算s1, s2的长度以及比较大小上面了。
  • 考虑到这里遍历的每一个i,j都是元素的起始位置,那么我们可以不用直接将返回结果存档在数组中,我们直接用一个boolean数组记录,当前的位置是不是回文串就行了
  • 在之前分析的情况一下; 如果头尾各缩进一个字符后的子字符串是回文串,那么当前字符也是回文串,一旦确定了一个字符串是回文串,那么就用i ,j 的索引去定下他的长度和起始位置,不断更新即可。
  • 在之前分析的情况二下,该位置不是回文串,那么没有利用价值,直接false即可,避免了之前的比较字符串长度的操作。
  • 优化后的代码如下:
    // 优化动态规划(继续优化)public String longestPalindrome4(String s){if(s.length() < 2) return s;int n = s.length();boolean[] dp = new boolean[n];int maxLen = 0;int maxS= 0;int maxE = 0;for (int i = n - 1; i >= 0; i--) {for (int j = n - 1; j >= 0; j--) {if(i > j){dp[j] = true;}else if(i == j) {dp[j] = true;}else if(s.charAt(i) == s.charAt(j) && dp[j-1]){dp[j] = true;if(maxLen < (j - i)){maxLen = j - i;maxS = i;maxE = j;}}else{dp[j] = false;}// 其他情况下全部置为false}}return maxLen == 0 ? s.substring(0,1) : s.substring(maxS,maxE + 1);}
  • 同样这段代码也AC过了,时间成本上没什么改变,空间降了4M的内存,还是有非常明显的提升。
  • 补充: 不知不觉写了这么多,以上展示的最优版本的代码虽然AC了,但是运行时间复杂还是比较高,这是我能用DP方法做的最好的效果了,欢迎大神批评指证,下面就附上这道题的最优解法Manacher算法,不过多解释,以后在写一个Manacher笔记好好理解一下,这里也给自己打个样。
    public String longestPalindrome4(String s) {if(s.equals("")) return s;String ret = tackleString(s);int n = ret.length();int[] bj = new int[n];   // 存放每个位置的回文半径int maxR = -1;           // 存放当前的最大回文右边界int cC = -1;             // 存放当前最大回文右边界对应的回文中心// 遍历s字符串for (int i = 0; i < n; i++) {// 如果当前位置在最大回文右边界内,则在当前位置与右边界之间的距离和前半部分的回文半径中去最小值// 如果在边界或者maxR外边,默认半径为1,然后进行中心扩展bj[i] = maxR > i ? Math.min(maxR - i,bj[(2 * cC) - i]) : 1;// 中心扩展while(i - bj[i] >= 0 && i + bj[i] < n){if(ret.charAt(i - bj[i]) == ret.charAt(i + bj[i])){bj[i]++;}else{break;}}// 扩展结束之后,更新maxR和cCif(i + bj[i] > maxR){maxR = i + bj[i];cC = i;}}// 找出当前半径数组中的最大半径int maxLen = bj[0];int center = 0;for (int i = 1; i < bj.length; i++) {if(bj[i] > maxLen){maxLen = bj[i];center = i;}}// 有了最大的回文半径和最大的回文中心,返回字符串即可int start = (center - maxLen + 1) / 2;int end = (center + maxLen) / 2;return s.substring(start,end);}// 改造字符串————给字符串加上虚轴private String tackleString(String s) {int n = s.length();String ret = "#";for (int i = 0; i < n; i++) {ret += s.charAt(i) + "#";}return ret;} 

字符串匹配问题

两道经典的leeCode题,都是hard级别的。
LeeCode10. 正则表达式匹配
好的,接下来继续撸第二道经典题,字符串匹配问题,问题如下:

问题一: 正则表达式匹配
给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 ‘.’ 和 ‘*’ 的正则表达式匹配。
’ . ’ 匹配任意单个字符
’ * ’ 匹配零个或多个前面的那一个元素

思路分析:

  • 首先还是建立暴力递归的思想:如果我们要求整体字符串s和p是否可以匹配,可以通过s和p的前缀字符串的匹配信息来推导出当前字符串的匹配情况。这里给一张图展示思路过程,绿色小旗子我们可以直接返回的结果,不需要计算,红色小旗子依赖子问题的求解。
  • 思路分析
  • 下面就是将思路用代码实现: 用两个指针cs,cp分别表示当前需要匹配s的前cs个字符串和p的前cp个字符串。
    // LeeCode10: 正则表达式匹配public boolean isMatch(String s, String p) {return getMatchRes(s, s.length(), p, p.length());}// cs当前选定s字符串取得前cs各字符串,cp指针表示选定当前p字符串的前cp个字符private boolean getMatchRes(String s, int cs, String p, int cp) {// 边界条件,如果p的指针cp走到头了,s的指针没走到头,肯定匹配不上返回falseif (cp == 0) return cs == 0;if (cp < 0) return false;// 如果s为null,if (cs == 0) {if (p.charAt(cp - 1) == '*') {return getMatchRes(s, cs, p, cp - 2);} else {return false;}}// 分情况讨论:// 情况一:如果当前的cs和cp位置的字符相等,或者cp位置的字符是'.',那么直接返回s和p格子前进一位的结果if (s.charAt(cs - 1) == p.charAt(cp - 1) || p.charAt(cp - 1) == '.') {return getMatchRes(s, cs - 1, p, cp - 1);}// 情况二,当前s和p位置的字符不相等时,两种可能性,一个时p为'*',还有一个p为字母if (p.charAt(cp - 1) == '*') {if ((cp < 2 || (s.charAt(cs - 1) != p.charAt(cp - 2)) && p.charAt(cp - 2) != '.')) {return getMatchRes(s, cs, p, cp - 2);} else {return getMatchRes(s, cs - 1, p, cp) || getMatchRes(s, cs, p, cp - 1) || getMatchRes(s, cs, p, cp - 2);}}return false;}

这把就很爽了,一次AC,不过必须要优化的,暴力递归的重复计算是不能接收的。继续分析递归代码:

  • 创建一个boolean[][]类型的dp数组,数组中存放的元素就是暴力递归每个步骤的结果,那么dp的属性很容易就可以定出来,dp = new boolean[s.length()][p.length(0)]
  • 从递归代码可以看出当前的格子依赖上面的格子,左一位的格子,以及左二位的格子,直接将格子替换到代码里面,如下:
class Solution {// LeeCode10: 正则表达式匹配public boolean isMatch(String s, String p) {if(p.equals("")) return s.equals("");// 创建dp数组boolean[][] dp = new boolean[s.length() + 1][p.length() + 1];// 遍历数组填数组for (int i = 0; i <= s.length(); i++) {for (int j = 0; j <= p.length(); j++) {if(i == 0 && j == 0){dp[i][j] = true;}else if(j == 0){dp[i][j] = false;}else if(i == 0){if(p.charAt(j - 1) == '*' && j - 1 > 0){dp[i][j] = dp[i][j - 2];}else{dp[i][j] = false;}}else if(s.charAt(i - 1) == p.charAt(j - 1) || p.charAt(j - 1) == '.') {dp[i][j] = dp[i - 1][j - 1];}else if(p.charAt(j - 1) == '*'){if (j > 1 && (s.charAt(i - 1) != p.charAt(j - 2)) && p.charAt(j - 2) != '.') {dp[i][j] = dp[i][j - 2];} else if(j == 1){dp[i][j] = false;} else{dp[i][j] = dp[i - 1][j] || dp[i][j - 1] || dp[i][j - 2];}}else{dp[i][j] = false;}}}return dp[s.length()][p.length()];}
}
  • 顺利AC,继续优化,boolean类型默认值为false,所有有很多没有必要写上去代码,优化结果如下:
class Solution {// LeeCode10: 正则表达式匹配public boolean isMatch(String s, String p) {if(p.equals("")) return s.equals("");// 创建dp数组boolean[][] dp = new boolean[s.length() + 1][p.length() + 1];// 遍历数组填数组for (int i = 0; i <= s.length(); i++) {for (int j = 0; j <= p.length(); j++) {if(i == 0 && j == 0){dp[i][j] = true;}else if(j == 0 || (j == 1 && p.charAt(j - 1) == '*')){dp[i][j] = false;}else if(i == 0){if(p.charAt(j - 1) == '*' && j - 1 > 0){dp[i][j] = dp[i][j - 2];}}else if(s.charAt(i - 1) == p.charAt(j - 1) || p.charAt(j - 1) == '.') {dp[i][j] = dp[i - 1][j - 1];}else if(p.charAt(j - 1) == '*'){if ((s.charAt(i - 1) != p.charAt(j - 2)) && p.charAt(j - 2) != '.') {dp[i][j] = dp[i][j - 2];} else{dp[i][j] = dp[i - 1][j] || dp[i][j - 1] || dp[i][j - 2];}}}}return dp[s.length()][p.length()];}
}
  • 走到这里,这个问题的执行效率已经非常高了,当然这里空间上还可以继续优化,不过对于列而言,需要依赖左边两个格子的状态,所以感觉有一定难度,这里不花时间去琢磨了,有机会再回头来试试。

下面再上一题如出一辙的题目,只是稍微变动了一点点。
LeeCode44. 通配符匹配
通配符匹配
思路分析: 这题起始和上一题是一样的,只是’*'可以匹配的范围变简单了一点。下图是暴力递归的分析思路。
通配符匹配
暴力递归代码如下:

class Solution {    public boolean isMatch(String s, String p) {return getRes(s,s.length(),p,p.length());}private boolean getRes(String s, int cs, String p, int cp) {if(cp == 0) return cs == 0;if(cs == 0) {return p.charAt(cp - 1) == '*' && getRes(s,cs,p,cp - 1);}if(s.charAt(cs - 1) == p.charAt(cp - 1) || p.charAt(cp - 1) == '?'){return getRes(s,cs - 1,p,cp - 1);}if(p.charAt(cp - 1) == '*'){return getRes(s,cs - 1,p,cp) || getRes(s,cs,p,cp - 1) || getRes(s,cs - 1,p,cp- 1);}return false;}
}

使用二维数组进行优化,代码如下:

    // 动态规划优化public boolean isMatch(String s, String p) {if(p.equals("")) return s.equals("");boolean[][] dp = new boolean[s.length() + 1][p.length() + 1];for (int i = 0; i <= s.length(); i++) {for (int j = 0; j <= p.length(); j++) {if(i == 0 && j == 0) {dp[i][j] = true;}else if(j == 0) {dp[i][j] = false;}else if(i == 0) {dp[i][j] = p.charAt(j - 1) == '*' && dp[i][j - 1];}else if(s.charAt(i - 1) == p.charAt(j - 1) || p.charAt(j - 1) == '?'){dp[i][j] = dp[i - 1][j - 1];}else if(p.charAt(j - 1) == '*'){dp[i][j] = dp[i - 1][j] || dp[i][j - 1] || dp[i - 1][j - 1];}}}return dp[s.length()][p.length()];} 

最长有效括号

32. 最长有效括号
最长有效括号
思路分析:

  • 这道题的关键就在于理解扩号字符串组成规律,摸清楚规律之后,编码就不是很么难事。
  • 有效括号字符串有几个性质: 左括号的数量必须等于右括号的数量,字符串必须以(开头,以)结尾。
  • 下面给出分析思路:
    最长有效括号
  • 根据分析的思路,上暴力递归代码:
    public int longestValidParentheses2(String s) {getRes(s,s.length() - 1);return maxLen;}int maxLen = 0;   // 放一个指针,用于记录最长的结果// c为字符串的索引,用于记录每个位置的最长有效括号的长度,只对右括号有效private int getRes(String s, int c) {// 扣边界if(c <= 0) return 0;if(s.charAt(c) == '('){  // 如果当前位置是'(',则缩进一位去考虑,此处直接返回0getRes(s,c - 1);return 0;}// 到这里c位置只可能是')'int ans = 0;if(s.charAt(c - 1) == '(') {ans = 2 + getRes(s,c - 2);       // 向前走两位} else {                                // 如果左边同样为右括号int left = getRes(s,c - 1);       // 用一个指针,记录他的值if(left != 0 && c - left - 1 >= 0 && s.charAt(c - left - 1 ) == '(') {ans = 2 + left + getRes(s,c-left-2);}}maxLen = Math.max(ans,maxLen);           // 每次走到这里都更新maxLen的值return ans;}
  • 同样的,老套路,暴力递归改成动态规划。
    // 暴力递归改动态规划public int longestValidParentheses2(String s) {int maxLen = 0;int[] dp = new int[s.length()];for (int i = 0; i < s.length(); i++) {if(i == 0 || s.charAt(i) == '('){dp[i] = 0;}else{if(s.charAt(i - 1) == '('){dp[i] = 2 + (i - 1 > 0 ? dp[i - 2] : 0);}else{if(dp[i - 1] != 0 && i - dp[i - 1] > 0 && s.charAt(i - dp[i - 1] - 1) == '('){dp[i] = 2 + dp[i - 1] + (i - dp[i - 1] - 1 > 0 ? dp[i - dp[i - 1] - 2] : 0);}// 其他情况默认为0}}maxLen = Math.max(maxLen,dp[i]);}return maxLen;}

编辑距离

再上一道难题,优势字符串问题,总结了一下规律,字符串问题大部分都可以用动态规划去求解。
LeeCode72. 编辑距离
编辑距离

  • 首先理解一下暴力递归的思路:
    编辑距离
  • 下面展示出暴力递归的代码,这里用了哈希表存储了中间重复计算的结果,在很多情况下这是一个非常好用的方法,但是他的效果和动态规划还是有非常大的差距。
    // 编辑距离public int minDistance2(String word1, String word2) {if(word1.equals("")) return word2.length();if(word2.equals("")) return word1.length();HashMap<String,Integer> memo = new HashMap<>();return  getRes(word1,0,word2,0,memo);}// 暴力递归,添加一个哈希map存储重复计算的结果private int getRes (String s1, int c1, String s2, int c2,HashMap<String,Integer> memo) {if(c1 == s1.length()) return s2.length() - c2;if(c2 == s2.length()) return s1.length() - c1;String key = c1 + "@" + c2;if(memo.containsKey(key)) return memo.get(key);int ans = 0;if(s1.charAt(c1) == s2.charAt(c2)){ans = getRes(s1,c1 + 1, s2, c2 + 1,memo);}else{int add = getRes(s1,c1 + 1, s2, c2,memo);int del = getRes(s1,c1,s2,c2 + 1,memo);int modify = getRes(s1,c1+1,s2,c2 + 1,memo);ans = 1 + Math.min(add,Math.min(del,modify));}memo.put(key,ans);return ans;}
  • 下面再用dp数组优化一下代码,构成动态规划。
class Solution {public int minDistance(String word1, String word2) {int len1 = word1.length();int len2 = word2.length();int[][] dp = new int[len1 + 1][len2 + 1];for (int i = len1; i >= 0; i--) {for (int j = len2; j >= 0; j--) {if(i == len1) {dp[i][j] = len2 - j;}else if(j == len2){dp[i][j] = len1 - i;}else if(word1.charAt(i) == word2.charAt(j)){dp[i][j] =dp[i + 1][j + 1];}else{int add = dp[i + 1][j];int del = dp[i][j + 1];int modify = dp[i + 1][j + 1];dp[i][j] =  1 + Math.min(add,Math.min(del,modify));}}}return dp[0][0];}
}

交错字符串

97. 交错字符串
继续上一道字符串的hard题。
交错字符串

  • 首先理一下暴力递归的思路:
    交错字符串
  • 交错字符串
  • 直接上暴力递归的代码
    // 交错字符串public boolean isInterleave(String s1, String s2, String s3) {if(s1.length() + s2.length() != s3.length()) return false;HashMap<String,Boolean> memo = new HashMap<>();return getRes(s1,0,s2,0,s3,0,memo);}private boolean getRes(String s1, int c1, String s2, int c2, String s3, int c3, HashMap<String, Boolean> memo) {if(c3 == s3.length()) return true;if(c1 == s1.length()) return s2.substring(c2,s2.length()).equals(s3.substring(c3,s3.length()));if(c2 == s2.length()) return s1.substring(c1,s1.length()).equals(s3.substring(c3,s3.length()));String key = c1 + "@" + c2;if(memo.containsKey(key)) return memo.get(key);if(s1.charAt(c1) ==  s3.charAt(c3)) {if(getRes(s1,c1+1,s2,c2,s3,c3+1,memo)){memo.put(key,true);return true;}}if(s2.charAt(c2) ==  s3.charAt(c3)) {if(getRes(s1,c1,s2,c2+1,s3,c3+1,memo)){memo.put(key,true);return true;}}memo.put(key,false);return false;}
  • 同样,动态规划优化如下
    // 交错字符串public boolean isInterleave(String s1, String s2, String s3) {if(s1.length() + s2.length() != s3.length()) return false;HashMap<String,Boolean> memo = new HashMap<>();return getRes(s1,0,s2,0,s3,0,memo);}private boolean getRes(String s1, int c1, String s2, int c2, String s3, int c3, HashMap<String, Boolean> memo) {if(c3 == s3.length()) return true;if(c1 == s1.length()) return s2.substring(c2,s2.length()).equals(s3.substring(c3,s3.length()));if(c2 == s2.length()) return s1.substring(c1,s1.length()).equals(s3.substring(c3,s3.length()));String key = c1 + "@" + c2;if(memo.containsKey(key)) return memo.get(key);if(s1.charAt(c1) ==  s3.charAt(c3)) {if(getRes(s1,c1+1,s2,c2,s3,c3+1,memo)){memo.put(key,true);return true;}}if(s2.charAt(c2) ==  s3.charAt(c3)) {if(getRes(s1,c1,s2,c2+1,s3,c3+1,memo)){memo.put(key,true);return true;}}memo.put(key,false);return false;}

最大子序和

53. 最大子序和
最大子序和

  • 这题的关键在于思想,构建合理的递推过程。
  • 数组中的最大子序和一定是连续的,且一定以某个位置的元素结尾,那么求出以每个位置结尾的最大元素和,然后找出所有位置中的最大值。直接用数组保存,也是一种动态规划的思想。
class Solution {public int maxSubArray(int[] nums) {int[] dp = new int[nums.length];dp[0] = nums[0];int ans = nums[0];for (int i = 1; i < nums.length; i++) {dp[i] = Math.max(dp[i - 1] + nums[i], nums[i]);ans = Math.max(ans, dp[i]);}return ans;}
}
  • 此题中当前格子只用到了前一个格子的信息,所以可以将dp数组直接用变量代替
   public int maxSubArray2(int[] nums) {int dp = nums[0];int ans = nums[0];for (int i = 1; i < nums.length; i++) {dp = Math.max(dp + nums[i], nums[i]);ans = Math.max(ans, dp);}return ans;}

不同路径问题

62. 不同路径
这道题也是非常经典的一道题,经常用作动态规划的开山之作。
不同路径
62. 不同路径
这道题也是非常经典的一道题,经常用作动态规划的开山之作。
不同路径
这题非常明确的提供了一张二维数组,非常容易联想到动态规划,那么关键就是理解它的递推逻辑。

  • 问题非常简单,小人只可以向下或者向右走,那么分别计算出向下和向右的路径和就可以求出最终的路径总数。
  • 下图提供分析思路:
    不同路径思路分析
  • 根据上图,构建暴力递归代码:
    public static int uniquePaths(int m, int n) {return getRes(0, 0, m, n);   // 从0,0位置出发}private static int getRes(int r, int c, int m, int n) {if (r == m - 1 || c == n - 1) return 1;return getRes(r + 1, c, m, n) + getRes(r, c + 1, m, n);  // 下边和加上右边和}
  • 感受一下暴力递归的华丽之处,代码非常简短,可惜超时
  • 非常明确的知道,当前的格子依赖它右边和下面的格子,构建动态规划。
    public static int uniquePaths(int m, int n) {int[][] dp = new int[m][n];for (int i = m - 1; i >= 0; i--) {for (int j = n - 1; j >= 0; j--) {if (i == m - 1 || j == n - 1) {dp[i][j] = 1;} else {dp[i][j] = dp[i + 1][j] + dp[i][j + 1];}}}return dp[0][0];}
  • 完全依赖递归的思路修改,这里因为我们没求一个结果的时候,都只用到了下面的格子和右边的格子,所以完全可以降低dp数组的维度,只用一个一维数组不断更新就可以实现了。
    public static int uniquePaths(int m, int n) {int[] dp = new int[n];for (int i = m - 1; i >= 0; i--) {for (int j = n - 1; j >= 0; j--) {if (i == m - 1 || j == n - 1) {dp[j] = 1;} else {dp[j] = dp[j] + dp[j + 1];}}}return dp[0];}
  • 直接将dp的中有关i的位置全部干掉就可以了。

接下来是这题的延伸:
63. 不同路径 II
不同路径Ⅱ

  • 在64题的基础上加了障碍物,所以要稍微改动一下递归的思路。
    不同路径 II
  • 改一下暴力递归的代码(超时)
class Solution {public int uniquePathsWithObstacles(int[][] obstacleGrid) {int m = obstacleGrid.length;int n = obstacleGrid[0].length;return getRes(obstacleGrid,0,0,m,n);}private int getRes(int[][] obstacleGrid, int r, int c, int m, int n) {if(obstacleGrid[r][c] == 1) return 0;if(r == m - 1 && c == n - 1) return 1;if(r == m - 1) return getRes(obstacleGrid,r,c + 1,m,n);  // 向右走if(c == n - 1) return getRes(obstacleGrid,r + 1,c,m,n);  // 向下走return getRes(obstacleGrid, r + 1, c, m, n) + getRes(obstacleGrid, r, c + 1, m, n);}
}
  • 补充一个哈希map提升性能的代码(可以AC)
class Solution {public int uniquePathsWithObstacles(int[][] obstacleGrid) {int m = obstacleGrid.length;int n = obstacleGrid[0].length;HashMap<String, Integer> memo = new HashMap<>();return getRes(obstacleGrid, 0, 0, m, n, memo);}private int getRes(int[][] obstacleGrid, int r, int c, int m, int n, HashMap<String, Integer> memo) {if (obstacleGrid[r][c] == 1) return 0;if (r == m - 1 && c == n - 1) return 1;String key = r + "@" + c;if (memo.containsKey(key)) return memo.get(key);if (r == m - 1) {int right = getRes(obstacleGrid, r, c + 1, m, n, memo);memo.put(key, right);return right;  // 向右走}if (c == n - 1) {int down = getRes(obstacleGrid, r + 1, c, m, n, memo);memo.put(key, down);return down;  // 向下走}int ans = getRes(obstacleGrid, r + 1, c, m, n, memo) + getRes(obstacleGrid, r, c + 1, m, n, memo);memo.put(key, ans);return ans;}
}
  • 递归改动态规划,直接按照递归的逻辑来
class Solution {public int uniquePathsWithObstacles(int[][] obstacleGrid) {int m = obstacleGrid.length;int n = obstacleGrid[0].length;int[][] dp = new int[m][n];for (int i = m - 1; i >= 0; i--) {for (int j = n - 1; j >= 0; j--) {if (obstacleGrid[i][j] == 1) {dp[i][j] = 0;continue;}if (i == m - 1 && j == n - 1) {dp[i][j] = 1;} else if (i == m - 1) {dp[i][j] = dp[i][j + 1];} else if (j == n - 1) {dp[i][j] = dp[i + 1][j];} else {dp[i][j] = dp[i + 1][j] + dp[i][j + 1];}}}return dp[0][0];}
}
  • 空间上一样可以优化
class Solution {public int uniquePathsWithObstacles(int[][] obstacleGrid) {int m = obstacleGrid.length;int n = obstacleGrid[0].length;int[] dp = new int[n];for (int i = m - 1; i >= 0; i--) {for (int j = n - 1; j >= 0; j--) {if (obstacleGrid[i][j] == 1) {dp[j] = 0;continue;}if (i == m - 1 && j == n - 1) {dp[j] = 1;} else if (i == m - 1) {dp[j] = dp[j + 1];} else if (j == n - 1) {dp[j] = dp[j];} else {dp[j] = dp[j] + dp[j + 1];}}}return dp[0];}
}

最小路径和

64. 最小路径和
继续来一道类似的题目:
最小路径和

  • 递归思路如下:
    最小路径和
  • 递归代码实现
class Solution {public int minPathSum(int[][] grid) {int m = grid.length;int n = grid[0].length;return getRes(grid,0,0,m,n);}private int getRes(int[][] grid, int r, int c, int m, int n) {if(r == m - 1 && c == n - 1) return grid[m - 1][n - 1];if(r == m - 1) return grid[r][c] + getRes(grid, r, c + 1, m, n);if(c == n - 1) return grid[r][c] + getRes(grid, r + 1, c, m, n);return grid[r][c] + Math.min(getRes(grid, r + 1, c, m, n),getRes(grid, r, c + 1, m, n));}
}
  • memo优化
class Solution {public int minPathSum(int[][] grid) {int m = grid.length;int n = grid[0].length;HashMap<String,Integer> memo = new HashMap<>();return getRes(grid,0,0,m,n,memo);}private int getRes(int[][] grid, int r, int c, int m, int n, HashMap<String,Integer> memo) {if(r == m - 1 && c == n - 1) return grid[m - 1][n - 1];String key = r + "@" + c;if(memo.containsKey(key)) return memo.get(key);if(r == m - 1) {int right = grid[r][c] + getRes(grid, r, c + 1, m, n, memo);memo.put(key,right);return right;}if(c == n - 1) {int down = grid[r][c] + getRes(grid, r + 1, c, m, n, memo);memo.put(key,down);return down;}int ans = grid[r][c] + Math.min(getRes(grid, r + 1, c, m, n, memo), getRes(grid, r, c + 1, m, n, memo));memo.put(key,ans);return ans;}
}
  • 动态规划
class Solution {public int minPathSum(int[][] grid) {int m = grid.length;int n = grid[0].length;int[][] dp = new int[m][n];for (int i = m - 1; i >= 0; i--) {for (int j = n - 1; j >= 0; j--) {if (i == m - 1 && j == n - 1) {dp[i][j] = grid[i][j];} else if (i == m - 1) {dp[i][j] = grid[i][j] + dp[i][j + 1];} else if (j == n - 1) {dp[i][j] = grid[i][j] + dp[i + 1][j];} else {dp[i][j] = grid[i][j] + Math.min(dp[i + 1][j], dp[i][j + 1]);}}}return dp[0][0];}
}
  • dp数组降维
class Solution {public int minPathSum(int[][] grid) {int m = grid.length;int n = grid[0].length;int[] dp = new int[n];for (int i = m - 1; i >= 0; i--) {for (int j = n - 1; j >= 0; j--) {if (i == m - 1 && j == n - 1) {dp[j] = grid[i][j];} else if (i == m - 1) {dp[j] = grid[i][j] + dp[j + 1];} else if (j == n - 1) {dp[j] = grid[i][j] + dp[j];} else {dp[j] = grid[i][j] + Math.min(dp[j], dp[j + 1]);}}}return dp[0];}
}

扰乱字符串

87. 扰乱字符串
判断给定的两个字符串是不是扰乱字符串:
扰乱字符串

  • 思路分析:
    思路分析
  • 找到边界条件:s1和s2不等长或者s1和s2的组成字符不相同,直接返回false
  • 找递归思路:将s1一份为二,形成多种不同的可能性,然后按个去和s2比较尝试,如果匹配上了,返回false;
  • 暴力递归代码如下:
    // memo技术优化public boolean isScramble(String s1, String s2){return process(s1,s2,new HashMap<String, Boolean>());}private boolean process(String s1, String s2, HashMap<String,Boolean> memo){String key = s1 + "@" + s2;if(memo.containsKey(key)){return memo.get(key);}// 首先如果两个字符串长度不一样一定为false’if(s1.equals(s2)) {memo.put(key,true);return true;}if(s1.length() != s2.length()){memo.put(key,false);return false;}int[] flag = new int[26];for (int i = 0; i < s1.length(); i++) {flag[s1.charAt(i) - 'a']++;flag[s2.charAt(i) - 'a']--;}for (int i = 0; i < flag.length; i++) {if(flag[i] != 0){memo.put(key,false);return false;  // 存在啊u不一样的字母}}// 进入暴力递归for (int i = 1; i < s1.length(); i++) {if(process(s1.substring(0,i),s2.substring(0,i),memo) && process(s1.substring(i),s2.substring(i),memo)){memo.put(key,true);return true;}if(process(s1.substring(0,i),s2.substring(s1.length() - i),memo)&& process(s1.substring(i),s2.substring(0,s2.length() - i),memo)){memo.put(key,true);return true;}}memo.put(key,false);return false;}
  • 动态规划优化:dp[i][j][k] : 表示以s1的j位置起始和s2的k位置起始的长度为i的字符串是否可以匹配
    // 扰乱字符串public boolean isScramble(String s1, String s2) {if (s1.equals(s2)) return true;int[] temp = new int[26];// 检查两个字符串构成字符集是否一样for (int i = 0; i < s1.length(); i++) {temp[s1.charAt(i) - 'a']++;temp[s2.charAt(i) - 'a']--;}for (int i = 0; i < 26; i++) {if (temp[i] != 0) return false;}int len = s1.length();// 创建辅助数组,dp[i][j][k] 表示长度为i时,s1从j 到 j+i的字符串是否可以和s2k 到 k + i 的字符串匹配// 返回结果为 dp[len][0][0];boolean[][][] dp = new boolean[len + 1][len][len];for (int i = 1; i <= len; i++) {for (int j = 0; j + i <= len; j++) {for (int k = 0; k + i <= len; k++) {if (i == 1) {dp[i][j][k] = s1.charAt(j) == s2.charAt(k);} else {for (int l = 1; l <= i; l++) {dp[i][j][k] = (dp[l][j][k] && dp[i - l][j + l][k + l]) || (dp[l][j][k + i - l] && dp[i - l][j + l][k]);if (dp[i][j][k]) {   // 如果找到了true,直接跳出来break;}}}}}}return dp[len][0][0];}

解码方法

解码方法
思路分析:

  • 字符串解码问题,同样用一个指针c来遍历字符串,计算每次遍历的结果累加,思路如下:
    思路分析
  • 暴力递归首先分析思路:
    public int numDecodings(String s) {int len = s.length();return getRes(s, 0, len);}private int getRes(String s, int c, int len) {// 结束条件if (c == len) return 1;if (s.charAt(c) == '0') return 0;if (c == len - 1) return 1;// 首先将当前的单个元素解码,计算后面的解码总数int n1 = getRes(s, c + 1, len);// 然后检查当前元素后面的元素和当前的元素组合是否可以构成有效字符int cur = (s.charAt(c) - '0') * 10 + (s.charAt(c + 1) - '0');return cur <= 26 ? n1 + getRes(s, c + 2, len) : n1;}
  • 动态规划优化(完全一样的思路,直接修改代码非常简单)
    public int numDecodings(String s) {int len = s.length();int[] dp = new int[len + 1];dp[len] = 1;for (int i = len - 1; i >= 0; i--) {if (s.charAt(i) == '0') {dp[i] = 0;} else if (i == len - 1) {dp[i] = 1;} else {int cur = (s.charAt(i) - '0') * 10 + (s.charAt(i + 1) - '0');dp[i] = cur <= 26 ? dp[i + 1] + dp[i + 2] : dp[i + 1];}}return dp[0];}

不同的二叉搜索树

不同的二叉搜索树

  • 此题用递归的思想求解,弄清楚递归的逻辑,就会变得特别简单。
    思路分析
  • 直接上递归代码
   public List<TreeNode> generateTrees(int n) {if(n < 1) return new ArrayList<>();return getRes(1, n);}private List<TreeNode> getRes(int l, int r) {List<TreeNode> ans = new ArrayList<>();if (l == r) {ans.add(new TreeNode(l));return ans;}if (l > r) {ans.add(null);return ans;}for (int i = l; i <= r; i++) {List<TreeNode> left = getRes(l, i - 1);   // 返回左边的结果List<TreeNode> right = getRes(i + 1, r);  // 返回右边的结果for (TreeNode t1 : right) {for (TreeNode t2 : left) {TreeNode root = new TreeNode(i);root.left = t2;root.right = t1;ans.add(root);}}}return ans;}

此题的另一种问法:
不同的二叉树

  • 此题的逻辑跟上一题是一样的,所以直接在上面的递归代码上修改即可
    public int numTrees(int n) {if(n < 1) return 0;return getRes(1, n);}private int getRes(int l, int r) {if (l == r) {return 1;}if (l > r) {return 1;}int ans = 0;for (int i = l; i <= r; i++) {int left = getRes(l, i - 1);   // 返回左边的结果int right = getRes(i + 1, r);  // 返回右边的结果ans += left * right;}return ans;}
  • 看到getRes中的一大堆指针变量,直接改为动态规划
    public int numTrees(int n) {if(n < 1) return 0;int[] dp = new int[n + 1];dp[0] = 1;dp[1] = 1;for (int i = 2; i <= n; i++) {for (int j = 1; j <= i; j++) {dp[i] += dp[j - 1] * dp[i - j];}}return dp[n];}

不同的子序列

115. 不同的子序列
不同的子序列

  • 思路分析: 传统的字符串匹配问题,考虑到每个元素的位置,当前元素可选可不选,分两条路走,构建出递归思想。
    不同的子序列
    public int numDistinct(String s, String t) {return getRes(s, t, 0, 0, s.length(), t.length());}private int getRes(String s, String t, int cs, int ct, int sl, int tl) {if (sl - cs < tl - ct) return 0;   // 长度不匹配直接返回0if (ct == tl) return 1;if (s.charAt(cs) != t.charAt(ct)) {// 当前位置的s和t匹配不上,那么cs肯定不选,+1return getRes(s, t, cs + 1, ct, sl, tl);} else {// 如果当前位置匹配上了, 那么两种情况的累加和,选和不选return getRes(s, t, cs + 1, ct, sl, tl) + getRes(s, t, cs + 1, ct + 1, sl, tl);}}
  • 将每一个子问题用动态规划的格子去表示,改成动态规划如下:这个dp不太好理解,但是结合动态规划修改就变得特别简单了
    public int numDistinct(String s, String t) {int lens = s.length();int lent = t.length();if(lens < lent) return 0;int[][] dp = new int[lens + 1][lent + 1];for (int i = lens; i >= 0; i--) {for (int j = lent; j >= 0; j--) {if(lens - i < lent - j){dp[i][j] = 0;}else if(j == lent){dp[i][j] = 1; }else{if(s.charAt(i) != t.charAt(j)){dp[i][j] = dp[i + 1][j];}else{dp[i][j] = dp[i + 1][j] + dp[i + 1][j + 1];}}}}return dp[0][0];}

查看全文
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

相关文章

  1. openwrt系统的mqtt使用与实现(已实现)

    mqtt的介绍(详情网上都有,我这里就简答的描述下)MQTT就是基于TCP/IP之上建立通信的,其底层的connect机制就是TCP/IP,如果我们对物联网这种应用场景,采用TCP/IP进行自下而上的开发,需要做大量其它的工作,比如client客户端,服务器端程序,以及对网络延迟、即时性以及用户…...

    2024/4/23 14:25:41
  2. linux 下完全卸载mysql

    RPM包安装方式的MySQL卸载 1关闭MySQL服务 [root@server bin]# service mysql stop Shutting down MySQL.. SUCCESS! [root@server bin]# service mysql statusERROR! MySQL is not running2删除MySQL对应的文件夹 查找文件 [root@server bin]# find / -name mysql /home/mys…...

    2024/4/23 14:25:48
  3. Unicode

    关于 ASCII编码 可以查看我的另外一篇博客 编码标准-ASCII 关于 汉字编码 可以参考我的另外一篇博客 编码标准-GB2312 GBK GB18030 Unicode起源作用层次方式UTF- Unicode字符集转换格式UTF-8UTF-16UTF-32字节序BOM(Byte Order Mark)分布环境字集使用简史 Unicode(统一码、万…...

    2024/4/18 6:01:13
  4. 图卷积神经网络GCN---池化层代表作

    GNN Pooling 文章目录GNN Pooling1 Deep Convolutional Networks on Graph-Structured Data2 Convolutional neural networks on graphs with fast localized spectral filtering3 An End-to-End Deep Learning Architecture for Graph Classification4 Hierarchical Graph Rep…...

    2024/4/23 14:25:41
  5. 牛客OJ:数字之和

    示例:输入: 4 12 97 39999 输出: 4 7 3 9 16 22 39 36public class AddSum {public static void main(String[] args) {Scanner sc = new Scanner(System.in);while(sc.hasNext()) {int n = sc.nextInt();if (n<=0 || n>=40000) {return ;}int m = n*n;int sum1 = 0;i…...

    2024/4/23 14:25:44
  6. 思路简单 二叉排序树(删除,插入,查找最大,最小结点操作)c++实现

    对于二叉排序树,基本的操作不做过多阐述,可看代码实现,对于四中调整操作,LL,RR,LR,RL其实前两中中动了两个点,而LR,RL可以看作是先LL或者RR在进行在进行一次LL或者RR#include<iostream> #include<algorithm> #include <stack> #include <vector&…...

    2024/4/23 14:25:37
  7. sql修改时间格式

    DATE_FORMAT(fici.apply_time,%Y-%m-%d %H:%i) AS applyTime,...

    2024/4/20 4:59:44
  8. 指针详解

    指针的定义和int、char、float等数据类型并无差别,本质上是一种数据类型,但存储的数据较为特殊、存放的是个地址。大家可能又要问了,那地址又是什么呢?在计算机中,所有的数据都是存放在存储器中的。 一般把存储器中的一个字节称为一个内存单元, 不同的数据类型所占用的内…...

    2024/4/17 7:42:46
  9. TP5 代码上传服务器之后验证码不显示问题

    代码上传服务器之后验证码不显示问题 tp5使用 think-captcha 后,本地环境正常的显示,可放到云服务器上却显示不了。 方法其实很简单,在 vendor/topthink/think-captcha/src/CaptchaController.php中加上这个ob_clean();这样就能够清除缓存区 代码: namespace think\captcha…...

    2024/4/15 8:33:35
  10. 解决MySQL导入数据报错1067 - Invalid default value for 字段名

    由于数据库版本升级,老数据库的数据文件导出以后,在新版本的数据库上执行会报错这种问题多是由于默认值不兼容引起的,我们可以通过修改sql_mode来解决这个问题考虑到职场小白,这篇会详细的介绍步骤,包括登录命令等(自己也是从小白过来的,很理解网上查到一篇资料,却不知…...

    2024/4/19 7:45:50
  11. redis初识

    redis是一种非关系型数据库,存储元素都是键值对类型的,值得数据结构本来有5种,之后redis又加入了三种。下面来讲一下redis的数据结构: redis的数据结构redis存储的是key-value键值对。key都是字符串,value是各种不同的数据结构。 五种常用的redis数据结构如下:字符串类型…...

    2024/4/17 7:43:52
  12. 实数数=预留数+库存数

    实数:就是仓库中物料真实数量,即实物数据;预留:业务发生而实际物权没有交换的数据;库存:就是仓库物料库存真实的逻辑数据,是满足订单交货必须需要的重要参考数据;三者关系:实数数=预留数+库存数wwww.auasoft.com 是你理想的选择====智能工厂规划与建设、MES原厂设计与…...

    2024/4/19 21:37:00
  13. MySQL45讲 读书笔记 12讲为什么我的MySQL会“抖”一下

    一 序本文属于极客时间MySQL45讲读书笔记系列。一条SQL语句,正常执行的时候特别快,但是有时也不知道怎么回事,它就会变得特别慢,并且这样的场景很难复现,它不只随机,而且持续时间还很短。这就像是数据库“抖”了一下。二 你的SQL语句为什么变“慢”了InnoDB在处理更新语句…...

    2024/4/22 3:02:22
  14. 【计算机组成原理】运算器和运算方法(习题)

    1.将下列数据转换为二进制数据 (A7.D3)H,(45.23)o,(35.75)10(A7.D3)_H,(45.23)_o,(35.75)_{10}(A7.D3)H​,(45.23)o​,(35.75)10​ 【解析】 (A7.D3)H=(10100111.11010011)2(A7.D3)_H=(1010 0111.1101 0011)_2(A7.D3)H​=(10100111.11010011)2​ (45.23)o=(100101.010011…...

    2024/4/17 7:43:52
  15. Leetcode | 面试刷题-简单级

    文章目录1.整数反转2.判定字符是否唯一前言:压根没想过还有刷编程题目的这一天,印象中从高一NOIP放弃开始就没刷过编程题了,大一的C没啥兴趣也就没刷,上机考啃老本险过。现在为了面个试还在leetcode上开始刷题,太悲惨了。先从简单级别的开始刷起吧。1.整数反转 介绍:给出…...

    2024/4/23 14:25:42
  16. 软件设计模式—策略模式

    前篇——软件设计模式-基础 前篇——软件设计模式-三种工厂模式 前篇——软件设计模式-装饰者模式 前篇——软件设计模式-单例模式 前篇——软件设计模式-原型模式 前篇——软件设计模式-命令模式 策略模式是一种行为型模式 目录1. 问题引入1.1 直接实现:1.2 这种实现的问题2.…...

    2024/4/23 14:25:41
  17. Java实验(04)

    文章目录一.汽车接口二.用户登录界面三.Student类与子类四.抽象类2020-4-1一.汽车接口(20分)接口与多态编程--汽车接口 题目描述 编程实现: (1) 汽车接口(Car):有两个方法, getName()、getPrice() (2) BMW类,实现汽车接口 (3) 奇瑞QQ类,实现汽车接口 (4) 桑塔那类,实现…...

    2024/4/23 14:25:40
  18. Java中hashcode核心算法

    hashcode 与 equal 方法的关系从规则的角度看如果两个对象相同, equals方法一定返回true,并且这两个对象的HashCode一定相同; 两个对象的HashCode相同,并不一定表示两个对象就相同,即equals()不一定为true,只能说明这两个对象在一个散列存储结构中。 两个对象的HashCode不…...

    2024/4/23 14:25:34
  19. vim 完整配置(解决了YouCompleteMe安装失败及用vimscript写添加文件头脚本)

    掉进linux的坑已经一年多了,最近给电脑加装了条固态,也顺便用这条固态重装了ubuntu,又恰好这段时间疫情在家的时间多,在配置环境时,也就多花了一些时间把vim完整的重新配置了。在这一篇文章之中,我将完整的介绍我的配置思路。最终配置文件在文章最后,(本篇默认你是知道…...

    2024/5/5 11:46:13
  20. LeetCode 121.买卖股票的最佳时机

    一,问题描述二,问题分析1.暴力枚举破解法,用一个二维数组memo[i][j]表示 第i天买入,第j天卖出股票的营收,j必须在i后面2.动态规划:minprice :表示当天为止的最低价,定义memo[i] :表示前 i 天的买卖股票的最高利润故由此的状态转移方程 memo[i] = max(memo[i-1],price…...

    2024/4/29 7:47:48

最新文章

  1. 自然语言处理(NLP)原理、用法、案例、注意事项

    自然语言处理&#xff08;Natural Language Processing&#xff0c;简称NLP&#xff09;是人工智能&#xff08;Artificial Intelligence&#xff0c;简称AI&#xff09;领域的一个重要分支&#xff0c;旨在让计算机能够理解、理解和生成人类语言。 NLP的原理是基于统计建模和机…...

    2024/5/6 8:39:15
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言&#xff0c;在此感激不尽。 权重和梯度的更新公式如下&#xff1a; w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/3/20 10:50:27
  3. Mac OS X系统中的隐藏文件夹 .fseventsd,.Spotlight-V100,.Trashes

    Mac OS X系统中的隐藏文件夹&#xff08;.fseventsd&#xff0c;.Spotlight-V100&#xff0c;.Trashes&#xff09;&#xff0c;务必谨慎操作。这些文件夹通常存储着系统的临时数据和缓存&#xff0c;对系统的正常运行至关重要。下面是对它们的说明以及如何处理的建议&#xff…...

    2024/5/4 2:52:49
  4. 基于springboot实现影城管理系统项目【项目源码+论文说明】

    基于springboot实现影城管理系统演示 摘要 随着现在网络的快速发展&#xff0c;网上管理系统也逐渐快速发展起来&#xff0c;网上管理模式很快融入到了许多生活之中&#xff0c;随之就产生了“小徐影城管理系统”&#xff0c;这样就让小徐影城管理系统更加方便简单。 对于本小…...

    2024/5/5 8:52:30
  5. 谷粒商城实战(008 缓存)

    Java项目《谷粒商城》架构师级Java项目实战&#xff0c;对标阿里P6-P7&#xff0c;全网最强 总时长 104:45:00 共408P 此文章包含第151p-第p157的内容 简介 数据库承担落盘&#xff08;持久化&#xff09;工作 拿map做缓存 这种是本地缓存&#xff0c;会有一些问题 分布…...

    2024/5/5 8:48:59
  6. 【外汇早评】美通胀数据走低,美元调整

    原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...

    2024/5/4 23:54:56
  7. 【原油贵金属周评】原油多头拥挤,价格调整

    原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...

    2024/5/4 23:54:56
  8. 【外汇周评】靓丽非农不及疲软通胀影响

    原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...

    2024/5/4 23:54:56
  9. 【原油贵金属早评】库存继续增加,油价收跌

    原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...

    2024/5/4 23:55:17
  10. 【外汇早评】日本央行会议纪要不改日元强势

    原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...

    2024/5/4 23:54:56
  11. 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响

    原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...

    2024/5/4 23:55:05
  12. 【外汇早评】美欲与伊朗重谈协议

    原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...

    2024/5/4 23:54:56
  13. 【原油贵金属早评】波动率飙升,市场情绪动荡

    原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...

    2024/5/4 23:55:16
  14. 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试

    原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...

    2024/5/4 23:54:56
  15. 【原油贵金属早评】市场情绪继续恶化,黄金上破

    原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...

    2024/5/6 1:40:42
  16. 【外汇早评】美伊僵持,风险情绪继续升温

    原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...

    2024/5/4 23:54:56
  17. 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势

    原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...

    2024/5/4 23:55:17
  18. 氧生福地 玩美北湖(上)——为时光守候两千年

    原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...

    2024/5/4 23:55:06
  19. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

    原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...

    2024/5/4 23:54:56
  20. 氧生福地 玩美北湖(下)——奔跑吧骚年!

    原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...

    2024/5/4 23:55:06
  21. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

    原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...

    2024/5/5 8:13:33
  22. 「发现」铁皮石斛仙草之神奇功效用于医用面膜

    原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...

    2024/5/4 23:55:16
  23. 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者

    原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...

    2024/5/4 23:54:58
  24. 广州械字号面膜生产厂家OEM/ODM4项须知!

    原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...

    2024/5/4 23:55:01
  25. 械字号医用眼膜缓解用眼过度到底有无作用?

    原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...

    2024/5/4 23:54:56
  26. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下&#xff1a;1、长按电脑电源键直至关机&#xff0c;然后再按一次电源健重启电脑&#xff0c;按F8健进入安全模式2、安全模式下进入Windows系统桌面后&#xff0c;按住“winR”打开运行窗口&#xff0c;输入“services.msc”打开服务设置3、在服务界面&#xff0c;选中…...

    2022/11/19 21:17:18
  27. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像&#xff08;每一幅图像的大小是564*564&#xff09; f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  28. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面&#xff0c;在等待界面中我们需要等待操作结束才能关机&#xff0c;虽然这比较麻烦&#xff0c;但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  29. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows&#xff0c;请勿关闭计算机”的提示&#xff0c;要过很久才能进入系统&#xff0c;有的用户甚至几个小时也无法进入&#xff0c;下面就教大家这个问题的解决方法。第一种方法&#xff1a;我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  30. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题&#xff0c;电脑时发现开机屏幕显现“正在配置Windows Update&#xff0c;请勿关机”(如下图所示)&#xff0c;而且还需求等大约5分钟才干进入系统。这是怎样回事呢&#xff1f;一切都是正常操作的&#xff0c;为什么开时机呈现“正…...

    2022/11/19 21:17:13
  31. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示&#xff0c;没过几秒后电脑自动重启&#xff0c;每次开机都这样无法进入系统&#xff0c;此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一&#xff1a;开机按下F8&#xff0c;在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  32. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况&#xff0c;就是电脑提示正在准备windows请勿关闭计算机&#xff0c;碰到这样的问题该怎么解决呢&#xff0c;现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法&#xff1a;1、2、依次…...

    2022/11/19 21:17:11
  33. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后&#xff0c;每次关机的时候桌面上都会显示一个“配置Windows Update的界面&#xff0c;提示请勿关闭计算机”&#xff0c;每次停留好几分钟才能正常关机&#xff0c;导致什么情况引起的呢&#xff1f;出现配置Windows Update…...

    2022/11/19 21:17:10
  34. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着&#xff0c;别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚&#xff0c;只能是考虑备份数据后重装系统了。解决来方案一&#xff1a;管理员运行cmd&#xff1a;net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  35. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题&#xff1a;电脑提示“配置Windows Update请勿关闭计算机”怎么办&#xff1f;win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢&#xff1f;一般的方…...

    2022/11/19 21:17:08
  36. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 &#xff0c;然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  37. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  38. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了&#xff0c;具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面&#xff0c;长时间没反应&#xff0c;无法进入系统。这个问题原来帮其他同学也解决过&#xff0c;网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  39. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法&#xff0c;并在最后教给你1种保护系统安全的好方法&#xff0c;一起来看看&#xff01;电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中&#xff0c;添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  40. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候&#xff0c;开启电脑发现电脑显示&#xff1a;配置windows更新失败&#xff0c;正在还原更改&#xff0c;请勿关闭计算机。。.这要怎么办呢&#xff1f;下面小编就带着大家一起看看吧&#xff01;如果能够正常进入系统&#xff0c;建议您暂时移…...

    2022/11/19 21:17:02
  41. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机&#xff0c;电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  42. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题&#xff0c;就是我们的win7系统在关机的时候&#xff0c;总是喜欢显示“准备配置windows&#xff0c;请勿关机”这样的一个页面&#xff0c;没有什么大碍&#xff0c;但是如果一直等着的话就要两个小时甚至更久都关不了机&#xff0c;非常…...

    2022/11/19 21:17:00
  43. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时&#xff0c;一般是您正对windows进行升级&#xff0c;但是这个要是长时间没有反应&#xff0c;我们不能再傻等下去了。可能是电脑出了别的问题了&#xff0c;来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  44. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况&#xff0c;当我们打开电脑之后&#xff0c;发现一直停留在一个界面&#xff1a;“配置Windows Update失败&#xff0c;还原更改请勿关闭计算机”&#xff0c;等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  45. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57