文章目录
LeetCode5 最长回文子串
最长回文子串
参考内容
本题使用的方法是动态规划,这里需要找的是最长回文子串,首先第一步,我们需要定义dp数组的含义,定义二维布尔数组dp[i][j]dp[i][j]数组表示:
-
字符串s[i...j]是否为回文子串
,如果是,dp[i][j]=true,如果不是dp[i][j]=false -
如何我们现在已经知道了dp[i+1][j-1]dp[i+1][j−1]了,那我们如何计算dp[i][j]dp[i][j]呢?通过观察,我们发现:
-
若s[i]==s[j]那么只要说明dp[i+1][j-1]是回文串,那么dp[i][j]也就是回文串
-
如果s[i]\ne s[j]s[i] =s[j]那么说明dp[i][j]dp[i][j]必定不是回文子串
-
package com.zj.IString;
import com.zj.CLinkedList.Problem2;
/**
* @Author Zhou Jian
* @Date 2020/8/17
* 组UI擦好难过回文子串
*/
public class Problem5 {
/**
* 动态规划
* dp[i][j] i--j内回文子串的长度
* dp[i][j] = dp[i+1][j-1]+2
* dp[i][j-1],dp[i+1][j]
* dp[i][i]=1
* i>j 0
*
* @param s
* @return
* https://leetcode-cn.com/problems/longest-palindromic-substring/solution/5-zui-chang-hui-wen-zi-chuan-dong-tai-gui-hua-jie-/
*/
public String longestPalindrome(String s) {
if(s==null) return "";
if(s.length()<2) return s;
// i,j子串是否为回文子串
boolean[][] dp = new boolean[s.length()][s.length()];
// 记录最长回文子串的长度
int max = 0;
// 记录最长回文子串的歧视以及结束位置
int left = 0;
int right = 0;
for(int i =0;i<s.length();i++) dp[i][i] =true;
for(int i= s.length()-1;i>=0;i--){
for(int j=i+1;j<s.length();j++){
// 遍历两边相等,则需要
if(s.charAt(i)==s.charAt(j)) {
// j和i相邻的时候
if(j - i == 1){
dp[i][j] = true;
}
else{
dp[i][j] = dp[i+1][j-1];
}
}
else {
dp[i][j] = false;
}
if((dp[i][j])&&(j-i+1>max)){
max = j-i+1;
left = i;
right = j;
}
}
}
return s.substring(left,right+1);
}
public static void main(String[] args) {
String s = "cbbd";
Problem5 problem5 = new Problem5();
String s1 = problem5.longestPalindrome(s);
System.out.println(s1);
}
}
LeetCode72编辑距离(两个字符串)
- dp[i][j] 字符串s[0…i]转换为字符串s[0…j]所需要最少的编辑次数
- 若s[i]==s[j]则不需要编辑,dp[i][j] = dp[i-1][j-1],否则需要进行修改
增加
,删除
,修改
package com.zj.IString;
/**
* @Author zhoujian
* @Date 2020/8/20 11:06
编辑距离
*/
public class Problem72 {
/**
* 编辑距离:
* 给你两个单词word1和word2,计算出将word1转换位word2所使用的最少操作数
* @param word1
* @param word2
* @return
*
* dp[i][j]字符串s1[0..i]转换位字符串s2[0...j]所需要的最少的编辑
dp[i][j] = dp[i-1][j-1]
dp[i-1][j]+1,dp[i][j-1]+1,dp[i-1][j-1]+1
将i添加 删除 修改一个数
实是楼上说的 思考过程形成 = 做过类似的题😂。。。这种两个字符串的,以两个字符串分别作为二维数组的横轴和纵轴,从(0, 0)开始到(len1,len2)模拟一遍二维数组的递推,会很有助于理解~
*/
public int minDistance(String word1, String word2) {
int[][] dp = new int[word1.length()+1][word2.length()+1];
for(int i=0;i<dp.length;i++) dp[i][0] = i;
for(int i=0;i<dp[0].length;i++) dp[0][i] = i;
for(int i=1;i<dp.length;i++){
for (int j=0;j<dp[0].length;j++){
// 若字符串相等则dp[i][j]=dp[i-1][j-1]
if(word1.charAt(i)==word2.charAt(j)){
dp[i][j] = dp[i-1][j-1];
}else{
dp[i][j]=Math.min(dp[i-1][j]+1,dp[i][j-1]+1);
dp[i][j] = Math.min(dp[i][j],dp[i-1][j-1]+1);
}
}
}
return dp[word1.length()][word2.length()];
}
}
LeetCode583 两个字符串的删除操作
-
转换为两个字符串中的最长公共子序列**
-
dp[i][j]为两个字符串中前s[0…i]与s2[0…j最长公共子序列长度,若s1[i]==s2[j]则dp[i][j]=dp[i-1][j-1]+1否则dp[i][j]=dp[i-1][j],dp[i][j-1]
package com.zj.IString;
/**
* @Author zhoujian
* @Date 2020/8/25 9:46
*/
public class Problem583 {
/**
* 两个字符串的删除操作:
* 给定两个单词word1和word2,找到使得word1和word2相同所需要的最小步数,
* 每部可以删除任意一个字符串中的一个字符
* 转换为求两个字符串最大公共子序列的问题
* @param word1
* @param word2
* @return
*/
public int minDistance(String word1, String word2) {
int m = word1.length();
int n = word2.length();
// dp[i][j]表示word[0....i-1]与word2[0...j-1]的最大公共子序列
int[][] dp = new int[m+1][n+1];
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
if(word1.charAt(i-1)==word2.charAt(j-1)){
dp[i][j]=dp[i-1][j-1]+1;
}else{
dp[i][j] = Math.max(dp[i-1][j],dp[i][j-1]);
}
}
}
return (m+n)-2*dp[m][n];
}
}
LeetCode139 单词拆分
- dp[i]表示前i个元素可以拆分成,则dp[i]= dp[j]&&字典中包含(s.subString(j,i-j))
// 采用动态规划
// dp[i] 表示前i个元素能拆分成
// dp[i] = dp[j]&&wordFictr.contains(s.substr(j,i-j)
public boolean wordBreak1(String s, List<String> wordDict){
boolean[] dp = new boolean[s.length()+1];
dp[0] = true;
for(int i=1;i<=s.length();i++){
for(int j=0;j<i;j++){
// dp[j]表示前j-1个字符可以拆分成
// j----i
if(dp[j]&&wordDict.contains(s.substring(j,i))){
dp[i] = true;
break;
}
}
}
return dp[s.length()];
}
LeetCode91 解码方法
- dp[i]表示前i个元素有dp[i]个拆分方法,则dp[i] = dp[i-2] +dpi-1
// 采用动态规划的方法
public int numDecodings1(String s){
int[] dp = new int[s.length()+1];
dp[0] = 1;
if(s.charAt(0)!=0) {
dp[1] = 1;
}else dp[1] = 0;
for(int i=2;i<dp.length;i++){
Character cur = s.charAt(i-1);
// 连续的两个数
String middlen = s.substring(i-2,i);
Integer value = Integer.parseInt(middlen);
//可以连续的取
if((value>=10&&value<=26)){
dp[i] = dp[i-2];
}
// 可以单独的取一个
if(cur>0) dp[i]+=dp[i-1];
}
return dp[s.length()];
}
LeetCode44 通配符匹配符
/**
* @Author zhoujian
* @Date 2020/8/20 7:55
* 通配符匹配
* 给定一个字符串(s)和一个字符模式(p)
* 实现一个支持?和*的通配符匹配
*/
public class Problem44 {
/**
* 分析:类似最长公共子串,最长公共子序列,编辑距离等求2个字符擦混(或数组)之间的某种关系的题目
* 一般来说都是用动态规划的解法
* https://leetcode-cn.com/problems/wildcard-matching/solution/zi-fu-chuan-dong-tai-gui-hua-bi-xu-miao-dong-by-sw/
* @param s
* @param p
* @return
* dp[i][j] 表示 p 的前 i 个字符和 s 的前 j 个字符是否匹配。
*/
public boolean isMatch(String s, String p) {
if(s==null&&p==null) return true;
if(s==null||p==null) return false;
int len1 = p.length(), len2 = s.length();
boolean[][] dp = new boolean[len1 + 1] [len2 + 1];
dp[0][0] = true;
// 处理一下匹配串 p 以若干个星号开头的情况。因为星号是可以匹配空串的:
for (int i = 1; i <= len1; i++) {
if (p.charAt(i - 1) != '*') {
break;
}
dp[i][0] = true;
}
for (int i = 1; i <= len1; i++) {
for (int j = 1; j <= len2; j++) {
// 如果 p[i - 1] == s[j - 1] 或 p[i - 1] == '?',表示当前的字符串是匹配的,dp[i][j] 可以从 dp[i - 1][j - 1] 转移而来。
if (p.charAt(i - 1) == s.charAt(j - 1) || p.charAt(i - 1) == '?') {
dp[i][j] = dp[i - 1][j - 1];
//如果 p[i - 1] == '*',这个位置可以匹配 0 到 若干个字符。那么 dp[i][j] 可以从 dp[i - 1][j] 转移而来(表示当前星号没有匹配字符),例如 ab, ab*
// 也可以从 dp[i][j - 1] 转移而来(表示当前星号匹配了当前的位置的字符)表示 * 代表的是非空字符,例如 abcd, ab*
// 因为只要任意一种匹配即可,所以这里是逻辑或的关系。
} else if (p.charAt(i - 1) == '*') {
dp[i][j] = dp[i - 1][j] | dp[i][j - 1];
}
}
}
return dp[len1][len2];
}
}