leecode更新

This commit is contained in:
markilue 2022-12-08 13:26:33 +08:00
parent 2d33b78f2c
commit 5f1ccb4541
1 changed files with 153 additions and 0 deletions

View File

@ -0,0 +1,153 @@
package com.markilue.leecode.dynamic;
import org.junit.Test;
import java.lang.reflect.Array;
import java.util.*;
/**
* @BelongsProject: Leecode
* @BelongsPackage: com.markilue.leecode.dynamic
* @Author: markilue
* @CreateTime: 2022-12-08 10:38
* @Description:
* TODO 力扣139题 单词拆分
* 给你一个字符串 s 和一个字符串列表 wordDict 作为字典请你判断是否可以利用字典中出现的单词拼接出 s
* 注意不要求字典中出现的单词全部都使用并且字典中的单词可以重复使用
* @Version: 1.0
*/
public class T17_WordBreak {
@Test
public void test(){
String s = "leetcode";
List<String> wordDict = new ArrayList<>(Arrays.asList("leet", "code"));
System.out.println(wordBreak(s,wordDict));
}
@Test
public void test1(){
String s = "applepenapple";
List<String> wordDict = new ArrayList<>(Arrays.asList("apple", "pen"));
System.out.println(wordBreak(s,wordDict));
}
/**
* 思路:实际上就是判断能重复用的wordDict能否构造出s
* TODO 动态规划五部曲:
* (1)dp定义:dp[i][j]使用[0-i]的wordDict能否构造出s[0-j]
* (2)dp状态转移方程:dp[j]=dp[j]||(dp[j-wordDict[i].length]&&wordDict[i]==s.subString(j-wordDict[i].length))
* (3)dp初始化:dp[0]=true
* (4)dp遍历顺序:跟两个for的顺序有关系,因为word可以反复利用,只需要判断最后的word
* (5)dp举例推导:
* 速度击败71.72%内存击败60.22%
* @param s
* @param wordDict
* @return
*/
public boolean wordBreak(String s, List<String> wordDict) {
boolean[] dp = new boolean[s.length()+1];
dp[0]=true;
for (int j = 0; j < dp.length; j++) {
for (int i = 0; i < wordDict.size(); i++) {
String word = wordDict.get(i);
if(j>=word.length()){
String b = s.substring(j-word.length(),j);
dp[j]=dp[j]||(dp[j-word.length()]&&word.equals(b));
}
}
}
return dp[s.length()];
}
/**
* 官方另一种背包找到之后直接break是精髓但是就看不出dp的状态转移方程了
* 速度击败92.41%内存击败87.5%
* @param s
* @param wordDict
* @return
*/
public boolean wordBreak2(String s, List<String> wordDict) {
boolean[] dp = new boolean[s.length() + 1];
dp[0] = true;
for (int i = 1; i <= s.length(); i++) {
for (String word : wordDict) {
int len = word.length();
if (i >= len && dp[i - len] && word.equals(s.substring(i - len, i))) {
dp[i] = true;
break;
}
}
}
return dp[s.length()];
}
/**
* 官方及代码随想录的的动态规划解法利用hashset加快速度
* 速度击败33.98%内存击败7.9%
* @param s
* @param wordDict
* @return
*/
public boolean wordBreak1(String s, List<String> wordDict) {
HashSet<String> set = new HashSet<>(wordDict);
boolean[] valid = new boolean[s.length() + 1];
valid[0] = true;
for (int i = 1; i <= s.length(); i++) {
for (int j = 0; j < i && !valid[i]; j++) {
if (set.contains(s.substring(j, i)) && valid[j]) {
valid[i] = true;
}
}
}
return valid[s.length()];
}
/**
* 回溯+记忆化搜索本质上就是把已经用过的东西存起来便于剪枝
* 速度击败79.1%内存击败24.38%
*/
private Set<String> set;
private int[] memo;
public boolean wordBreak3(String s, List<String> wordDict) {
memo = new int[s.length()];
set = new HashSet<>(wordDict);
return backtracking(s, 0);
}
public boolean backtracking(String s, int startIndex) {
// System.out.println(startIndex);
if (startIndex == s.length()) {
return true;
}
if (memo[startIndex] == -1) {
return false;
}
for (int i = startIndex; i < s.length(); i++) {
String sub = s.substring(startIndex, i + 1);
// 拆分出来的单词无法匹配
if (!set.contains(sub)) {
continue;
}
boolean res = backtracking(s, i + 1);
if (res) return true;
}
// 这里是关键找遍了startIndex~s.length()也没能完全匹配标记从startIndex开始不能找到
memo[startIndex] = -1;//关键在于这里添加了记忆即从startIndex开始是不能找到以后到这就不用继续往下回溯了
return false;
}
}