单词拆分2

116 阅读2分钟

动态规划

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class Main {


    public static void main(String[] args) {

        Main main = new Main();
        List<String> tt = new ArrayList<>();
        tt.add("cat");
        tt.add("cats");
        tt.add("and");
        tt.add("dog");
        tt.add("sand");
        main.wordBreak("catsanddog", tt);

    }

    public List<String> wordBreak(String s, List<String> wordDict) {

        if (wordDict.contains(s)) {
            List<String> temp = new ArrayList<>();
            temp.add(s);
            return temp;
        }
        Map<Integer, List<String>> dpTable = new HashMap<>();
        for (int i = 0; i < s.length(); i ++) {
            for (int j = 0; j <= i; j ++) {
                String temp = s.substring(j , i + 1);
                if (wordDict.contains(temp) && (j - 1 < 0 || dpTable.containsKey(j - 1))) {
                    List<String> tempList;
                    if (dpTable.containsKey(j - 1)) {
                        tempList = dpTable.get(j - 1);
                        tempList = tempList.stream()
                                .map(ss -> ss + " " + temp)
                                .collect(Collectors.toList());
                    } else {
                        tempList = new ArrayList<>();
                        tempList.add(temp);
                    }
                    if (dpTable.containsKey(i)) {
                        tempList.addAll(dpTable.get(i));
                    }
                    dpTable.put(i, tempList);

                }
            }
        }
        if (dpTable.get(s.length() - 1) == null) {
            return new ArrayList<>();
        }
        return dpTable.get(s.length() - 1);

    }
}

基本思路

  1. 在单词拆分1的基础上, 利用一个map保存每个位置上可能的结果, 每次的结果需要将之前的结果进行拼接得到即可

缺点

  1. 内存超限了

动态规划2

import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors;

public class Main {

public static void main(String[] args) {

    Main main = new Main();
    List<String> tt = new ArrayList<>();
    tt.add("cat");
    tt.add("cats");
    tt.add("and");
    tt.add("dog");
    tt.add("sand");
    main.wordBreak("catsanddog", tt);

}

public List<String> wordBreak(String s, List<String> wordDict) {
    if (wordDict.contains(s)) {
        List<String> temp = new ArrayList<>();
        temp.add(s);
        return temp;
    }

    boolean [] dp = new boolean[s.length()];
    for (int i = 0; i < s.length(); i ++) {
        for (int j = 0; j <= i; j ++) {
            String temp = s.substring(j , i + 1);
            if (wordDict.contains(temp) && (j - 1 < 0 || dp[j - 1])) {
                dp[i] = Boolean.TRUE;
                break;
            }
        }
    }

    if (!dp[s.length() - 1]) {
        return new ArrayList<>();
    }


    Map<Integer, List<String>> dpTable = new HashMap<>();
    for (int i = 0; i < s.length(); i ++) {
        for (int j = 0; j <= i; j ++) {
            String temp = s.substring(j , i + 1);
            if (wordDict.contains(temp) && (j - 1 < 0 || dpTable.containsKey(j - 1))) {
                List<String> tempList;
                if (dpTable.containsKey(j - 1)) {
                    tempList = dpTable.get(j - 1);
                    tempList = tempList.stream()
                            .map(ss -> ss + " " + temp)
                            .collect(Collectors.toList());
                } else {
                    tempList = new ArrayList<>();
                    tempList.add(temp);
                }
                if (dpTable.containsKey(i)) {
                    tempList.addAll(dpTable.get(i));
                }
                dpTable.put(i, tempList);

            }
        }
    }
    if (dpTable.get(s.length() - 1) == null) {
        return new ArrayList<>();
    }
    return dpTable.get(s.length() - 1);

}

}

基本思路

  1. 在1的基础上首先进行一轮动态规划, 判断整个字符串是否能够被拆分, 如果能, 再进行拆分, 避免了内存不足的问题

回溯1

import java.util.ArrayList;
import java.util.List;

public class Main {


    public static void main(String[] args) {

        Main main = new Main();
        List<String> tt = new ArrayList<>();
        tt.add("cat");
        tt.add("cats");
        tt.add("and");
        tt.add("dog");
        tt.add("sand");
        main.wordBreak("catsanddog", tt);

    }


    String s;
    List<String> wordDict;
    List<List<String>> ans = new ArrayList<>();
    public List<String> wordBreak(String s, List<String> wordDict) {
        this.s = s;
        this.wordDict = wordDict;


        digui(0, new ArrayList<>());
        List<String> result = new ArrayList<>();
        for (List<String> temp : ans) {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < temp.size(); i ++) {
                if (i == 0) {
                    sb.append(temp.get(i));
                } else {
                    sb.append(" " + temp.get(i));
                }
            }

            result.add(sb.toString());
        }
        return result;

    }

    public void digui(int start, List<String> path) {
        if (start == s.length()) {
            ans.add(new ArrayList<>(path));
            return;
        }
        for (int i = start; i < s.length(); i ++) {
            String temp = s.substring(start, i + 1);
            if (wordDict.contains(temp)) {
                // 作出选择
                path.add(temp);
                digui(i + 1, path);
                // 撤销选择
                if (path.size() > 0) {
                    path.remove(path.size() - 1);
                }
            }
        }
    }
}

基本思路

  1. 如果是寻找所有解的情况,那么用递归一定可以实现的

  2. 用递归的思想, 从字符串头部开始寻找字典中的单词, 寻找到就递归一次, 继续寻找之后字符串中的单词, 记录寻找到的结果

缺点

  1. 暴力递归, 没有做记忆的部分, 递归了很多重复的部分, 需要剪枝, 减少一些没有必要的递归

回溯2

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Main {


    public static void main(String[] args) {

        Main main = new Main();
        List<String> tt = new ArrayList<>();
        tt.add("cat");
        tt.add("cats");
        tt.add("and");
        tt.add("dog");
        tt.add("sand");
        main.wordBreak("catsanddog", tt);

    }


    String s;
    List<String> wordDict;
    List<List<String>> ans = new ArrayList<>();
    Map<Integer, List<List<String>>> tmepPathMap = new HashMap<>();
    public List<String> wordBreak(String s, List<String> wordDict) {
        this.s = s;
        this.wordDict = wordDict;
        digui(0);
        List<String> result = new ArrayList<>();
        for (List<String> temp : tmepPathMap.get(0)) {
            StringBuilder sb = new StringBuilder();
            for (int i = temp.size() - 1; i >= 0; i --) {
                if (i == temp.size() - 1) {
                    sb.append(temp.get(i));
                } else {
                    sb.append(" " + temp.get(i));
                }
            }

            result.add(sb.toString());
        }
        return result;

    }

    public List<List<String>> digui(int start) {
        if (!tmepPathMap.containsKey(start)) {
            // 当前问题的结果
            List<List<String>> nowStartList = new ArrayList<>();
            if (start == s.length()) {
                // 通过添加空list的方式, 保证递归返回的tempList如果有值, 就是正常的
                // 否则返回的就是空
                nowStartList.add(new ArrayList<>());
            }
            for (int i = start; i < s.length(); i ++) {
                String temp = s.substring(start, i + 1);
                if (wordDict.contains(temp)) {
                    List<List<String>> tempList = digui(i + 1);
                    for (List<String> ttttt: tempList) {
                        List<String> cloneList= new ArrayList<>(ttttt);
                        cloneList.add(temp);
                        nowStartList.add(cloneList);
                    }
                }
            }
            tmepPathMap.put(start, nowStartList);
        }

        return tmepPathMap.get(start);
    }
}

基本思路

  1. 主要思路是递归, 然后想办法用一个map来记录j....n-1所有肯能的单词表的情况, 避免重复计算的问题

  2. 为了记忆, 所以递归的返回值只能是单个子问题的答案, 即只能是List<List>

  3. 如果字符串的最后构成了单词, 那么返回一个空list, 用来表示次此次递归到底的结论是有效的. 否则返回null, 这样就不会记录, 这一点很重要