wordcut II | Тематический месяц Go

Go

[Месяц изучения темы Golang] На выходных я попробовал ответить на несколько вопросов по динамическому программированию и отправил очень деликатную учебную версию. Ответ был очень хорошим. Далее я буду использовать два языка для вопросов по кодированию и чистке, а именно GO и JAVA , ребята, продолжайте писать вопросы.

Если вы не знакомы с динамическим программированием, перейдите к этой статье.\color{red}{Если вы не знакомы с динамическим программированием, перейдите к этой статье~}

Живите много дней - десять последовательных динамических программ - сверхтонкий анализ |

это классика,Расширенные возможности 😄😄😄 \color{green}{Этот вопрос очень классический, и его нужно прояснить 😄 😄 😄 ~}

Какие вопросы вы можете решить с помощью динамического программирования?

1. Считать

  • Сколько способов попасть в правый нижний угол
  • Сколькими способами можно выбрать k чисел да и сложить

2. Найдите максимальное и минимальное значения

  • Максимальная сумма чисел пути из левого верхнего угла в правый нижний угол
  • самая длинная восходящая длина подпоследовательности

3. Ищите существования

  • В игре со сбором камней обязательно ли выиграет тот, кто сделает первый ход?
  • Можно ли выбрать k чисел так, чтобы сумма была суммой

4. Комплексное применение

  • динамическое программирование + хэш
  • динамическое программирование + рекурсия
  • ...

leetcode 140. Разделение слов II

Имея непустую строку s и словарь wordDict, содержащий список непустых слов, добавьте пробелы в строку, чтобы составить предложение, в котором все слова в предложении находятся в словаре. Верните все эти возможные предложения.

проиллюстрировать:

Слова из словаря можно использовать повторно при разделении.

Можно считать, что в словаре нет повторяющихся слов.

Пример 1:

войти:

s = "catsanddog"

wordDict = ["cat", "cats", "and", "sand", "dog"]

выход:

[   "cats and dog",   "cat sand dog" ]

Пример 2:

войти:

s = "pineapplepenapple"

wordDict = ["apple", "pen", "applepen", "pine", "pineapple"]

выход:

[   "pine apple pen apple",   "pineapple pen apple",   "pine applepen apple" ]

Объяснение: Обратите внимание, что вы можете повторно использовать слова из словаря.

Пример 3:

войти:

s = "catsandog"

wordDict = ["cats", "dog", "sand", "and", "cat"]

выход:

[]


--

Это сложный вопрос, сначала прочтитеслово разделить я

❤️❤️❤️❤️

2.1 Компонент динамического программирования 1: определение состояния

Проще говоря, при решении динамического программирования вам нужно открыть массив, что представляет собой каждый элемент массива f[i] или f[i][j] аналогично тому, что представляют x, y, z в математических задачах.

последний шаг

Мы определяем dp[i], чтобы указать, может ли строка s[0..i−1], состоящая из первых i символов строки s, быть разделена пробелами на несколько слов, которые встречаются в словаре.

Если разделение пробелов выполняется в позиции j, то чтобы определить, можно ли разбить s на одно или несколько слов, которые появляются в словаре пробелами, мы можем определить s[0 to j - 1] и s[j to i - 1 ] есть ли две части в словаре

s[0 to j-1] мы можем использовать идею динамического программирования, я храню предыдущий словарь

Например, s = "leetcode", wordDict = ["lee", "t", "code"]

[0 to j - 1] может относиться к двум словам ["lee", "t"], поскольку dp[0] = true, d[1] есть в словаре, поэтому уравнение перехода может быть следующим:

1.2 Компонент динамического программирования 2: уравнения перехода

dp[i]=dp[j] && проверить(s[j..i−1])

Проверка здесь может быть хеш-оценкой, находится ли перехваченная строка в массиве словаря.

1.3 Компонент динамического программирования 3: начальные условия и граничные случаи

dp[0] = true; пустая строка является правильной.

1.4 Компонент динамического программирования 4: Порядок вычислений

Вычислите по очереди, используйте i, чтобы пройти от 0 до j.

Пока мы просто знаем, как строки в словаре, как строить предложения, конечно, мы должны полагаться на поиск с возвратом.+hash(Память)😄😄😄 \color{green}{На данный момент мы просто знаем, что строка находится в словаре, как составить предложение, конечно, мы должны полагаться на поиск с возвратом + хэш (память) 😄 😄 😄 ~}

Код ссылки

языковая версия ГО

func wordBreak(s string, wordDict []string) (sentences []string) {
    wordSet := map[string]struct{}{}
    for _, w := range wordDict {
        wordSet[w] = struct{}{}
    }

    n := len(s)
    dp := make([][][]string, n)
    var backtrack func(index int) [][]string
    backtrack = func(index int) [][]string {
        if dp[index] != nil {
            return dp[index]
        }
        wordsList := [][]string{}
        for i := index + 1; i < n; i++ {
            word := s[index:i]
            if _, has := wordSet[word]; has {
                for _, nextWords := range backtrack(i) {
                    wordsList = append(wordsList, append([]string{word}, nextWords...))
                }
            }
        }
        word := s[index:]
        if _, has := wordSet[word]; has {
            wordsList = append(wordsList, []string{word})
        }
        dp[index] = wordsList
        return wordsList
    }
    for _, words := range backtrack(0) {
        sentences = append(sentences, strings.Join(words, " "))
    }
    return
}




dfsЭтот метод стоит внимания. 😄😄😄 \color{red}{dfs Этот метод заслуживает внимания. 😄 😄 😄 ~}

Java-версия

 public List<String> wordBreak2(String s, List<String> wordDict) {
        // 为了快速判断一个单词是否在单词集合中,需要将它们加入哈希表
        Set<String> wordSet = new HashSet<>(wordDict);
        int len = s.length();

        // 第 1 步:动态规划计算是否有解
        // dp[i] 表示「长度」为 i 的 s 前缀子串可以拆分成 wordDict 中的单词
        // 长度包括 0 ,因此状态数组的长度为 len + 1
        boolean[] dp = new boolean[len + 1];
        // 0 这个值需要被后面的状态值参考,如果一个单词正好在 wordDict 中,dp[0] 设置成 true 是合理的
        dp[0] = true;

        for (int right = 1; right <= len; right++) {
            // 如果单词集合中的单词长度都不长,从后向前遍历是更快的
            for (int left = right - 1; left >= 0; left--) {
                // substring 不截取 s[right],dp[left] 的结果不包含 s[left]
                if (wordSet.contains(s.substring(left, right)) && dp[left]) {
                    dp[right] = true;
                    // 这个 break 很重要,一旦得到 dp[right] = True ,不必再计算下去
                    break;
                }
            }
        }

        // 第 2 步:回溯算法搜索所有符合条件的解
        List<String> res = new ArrayList<>();
        if (dp[len]) {
            Deque<String> path = new ArrayDeque<>();
            dfs(s, len, wordSet, dp, path, res);
            return res;
        }
        return res;
    }

    /**
     * s[0:len) 如果可以拆分成 wordSet 中的单词,把递归求解的结果加入 res 中
     *
     * @param s
     * @param len     长度为 len 的 s 的前缀子串
     * @param wordSet 单词集合,已经加入哈希表
     * @param dp      预处理得到的 dp 数组
     * @param path    从叶子结点到根结点的路径, 既然是队列,先进后出,需要保存叶子结点到根结点的路径,那么先进的是根节点
     * @param res     保存所有结果的变量
     */
    private void dfs(String s, int len, Set<String> wordSet, boolean[] dp, Deque<String> path, List<String> res) {
        if (len == 0) { // 
            res.add(String.join(" ",path));
            return;
        }

        // 可以拆分的左边界从 len - 1 依次枚举到 0
        for (int i = len - 1; i >= 0; i--) {
            String suffix = s.substring(i, len);
            // 保证根节点或者叶子节点在集合中,dp[i] = true,表示并不会重复出现
            // 因此len = 0时,即可将结果添加
            if (wordSet.contains(suffix) && dp[i]) {  
                path.addFirst(suffix);  // 先进根节点,再进叶子节点
                dfs(s, i, wordSet, dp, path, res); // i--
                path.removeFirst(); // 先移除叶子节点,在移除根节点
            }
        }
    }

    @Test
    public void iswordBreak2() {
        ArrayList a = new ArrayList<String>();
        a.add("ab");
        a.add("c");
        a.add("a");
        a.add("bc");
        List<String> i = wordBreak2("abc", a);
        Assert.assertNotNull(i);
    }




❤️❤️❤️❤️

Большое спасибо, что смогли увидеть эту статью.Если эта статья хорошо написана и вы думаете, что есть что-то, пожалуйста, ставьте лайк👍 Следуйте ❤️ Пожалуйста, поделитесь 👥 Это действительно полезно для меня, красавчик Оппа! ! !

Если в этом блоге есть какие-либо ошибки, пожалуйста, критикуйте и советуйте, это очень ценится!

В конце статьи мы недавно составили материал для интервью «Руководство по прохождению интервью по Java», в котором рассматриваются основные технологии Java, JVM, параллелизм Java, SSM, микросервисы, базы данных, структуры данных и многое другое. Как получить: GitHub github.com/Tingyu-Note…, следуйте официальной учетной записи для получения большего контента: Tingyu Notes, которые будут предоставляться один за другим.