首页 > 试题广场 >

拆分词句

[编程题]拆分词句
  • 热度指数:61071 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 32M,其他语言64M
  • 算法知识视频讲解
给定一个字符串s和一组单词dict,判断s是否可以用空格分割成一个单词序列,使得单词序列中所有的单词都是dict中的单词(序列可以包含一个或多个单词)。
例如:
给定s=“nowcode”;
dict=["now", "code"].
返回true,因为"nowcode"可以被分割成"now code".
//来个用set的
import java.util.*;
//定义一个set 把符合f(x) = true 的 x 放入set中
//1状态f(x)表示s.substring(0, x)是否可以符合条件
//2状态转移:遍历set中的所有元素num 若存在s.substring(num, x)在dic中 则为true 否则false
//3临界值:set为空时,只看s.substring(0, x)是否在dic中
public class Solution {
    public boolean wordBreak(String s, Set<String> dict) {
        //使用set来剪枝
        Set<Integer> set = new HashSet<>();
        set.add(0);
        for (int i = 0; i <= s.length(); i++) {
            Iterator<Integer> iter = set.iterator();
            boolean contains = false;
            while (iter.hasNext()) {
                int j = iter.next();
                if (dict.contains(s.substring(j, i))) {
                        contains = true;
                        break;
                } 
            }
            if (contains) {
                set.add(i);
            }
        }
            return set.contains(s.length());
    }
}

发表于 2021-12-13 17:58:30 回复(0)
// 动态规划
import java.util.*;
public class Solution {
    public boolean wordBreak(String s, Set<String> dict) {
        int N = s.length();
        // canBreak[i] 表示 s[0...i) 区间是否有若干个子串存在于 dict 中
        boolean[] canBreak = new boolean[N + 1];
        // 初始条件 canBreak[0] 为 true, s[0,0)区间为空区间, 想象为空集是任何集合的子集
        canBreak[0] = true;
        
        // 更新canBreak[1...N]的状态
        for(int i = 1; i <= N; i++) {
            // 记录从 s[j...i) 区间是否有若干子串存在于 dict 中
            for(int j = 0; j < i; j++) {
                // canBreak[j] 纪录了 s[0...j) 区间是否有若干个子串存在于 dict 中
                if(canBreak[j] && dict.contains(s.substring(j,i))) {
                    canBreak[i] = true;
                    break;
                } 
            }
        }
        return canBreak[N]; // canBreak[N] 纪录了 s[0...N) 区间是否有若干个子串存在于 dict 中
    }
}

// DFS
import java.util.*;
public class Solution {
    public boolean wordBreak(String s, Set<String> dict) {
        return canBreak(s, dict, 0, new int[s.length()]);
    }
    
    private boolean canBreak(String s, Set<String> dict, int b, int[] visited) {
        // [b,s.length) 此时为空区间
        if(b == s.length())
            return true;
        // visited纪录 s[b...s.length) 区间的访问状态
        // 0 -- 未访问
        // 1 -- s[b...s.length) 区间有若干子串存在于 dict 中
        // -1 -- s[b...s.length) 区间没有若干子串存在于dict 中
        if(visited[b] != 0)
             return visited[b] == 1;
        
        // 判断 s[b...s.length) 区间是否有若干子串存在于 dict 中
        for(int i = b + 1; i <= s.length(); i++) {
            // 如果 s[b...i) 包含于 dict 中, 则递归剩余区间查看是否有若干字串存在于 dict 中 
            if(dict.contains(s.substring(b,i)) && canBreak(s, dict, i, visited)) {
                // 如果 s[b...i) 和 s[i...s.length) 都为 true, 那么表示 s[b...s.length] 区间有若干子串存在于 dict 中
                visited[b] = 1;  // 更新状态
                return true;     
            }
        }
        // 如果循环走完, 为进入 if 则表示s[b...s.length] 区间没有若干子串存在于 dict 中
        visited[b] = -1;  // 更新状态
        return false;
    }
}

发表于 2021-10-31 19:47:47 回复(0)
/**
 * 方法:动态规划
 * 状态:
 *      子状态:前1,2,3,...,n个字符能否根据词典中的词被成功分词
 *      F(i): 前i个字符能否根据词典中的词被成功分词
 * 状态递推:
 *      F(i): true{j <i && F(j) && substr[j+1,i]能在词典中找到}&nbs***bsp;false
 *      在j小于i中,只要能找到一个F(j)为true,并且从j+1到i之间的字符能在词典
 *      中找到,则F(i)为true
 * 初始值:
 *      对于初始值无法确定的,可以引入一个不代表实际意义的空状态,作为状态的起始
 *      空状态的值需要保证状态递推可以正确且顺利的进行,到底取什么值可以通过简单
 *      的例子进行验证
 *      F(0) = true
 * 返回结果:F(n)
 */
public class Solution {
    public boolean wordBreak(String s, Set<String> dict) {
        boolean[] canBreak = new boolean[s.length() + 1];
        // 初始化F(0) = true
        canBreak[0] = true;
        for (int i = 1; i <= s.length(); i++) {
            for (int j = 0; j < i; j++) {
                // F(i): true{j < i && F(j) && substr[j+1,i]能在词典中找到}&nbs***bsp;false
                // 第j+1个字符的索引为j
                if (canBreak[j] && dict.contains(s.substring(j,i))) {
                    canBreak[i] = true;
                    break;
                }
            }
        }
        return canBreak[s.length()];
    }
}

编辑于 2020-07-30 19:14:46 回复(0)
import java.util.*;
public class Solution {
    static boolean sign = false;
    public boolean wordBreak(String s, Set dict) {
        if(s.length() == 0 || dict == null)return false;
        return DFS(s,dict);
    }
    public boolean DFS(String s, Set dict){
        if(s.length() == 0)sign = true;
        if(sign)return true;
        for(String str:dict){
            if(sign)return true;
            if(s.startsWith(str)){
                DFS(s.substring(str.length()),dict);
            }
        }
        return sign;
    }
}

这个样例数据很有问题啊,明明输出没问题,本地没问题,到牛客上就过不了(他自己的显示也有问题),看了好多其他的答案都可以a,不知道自己的问题出在哪里

图片说明

发表于 2020-03-28 15:50:48 回复(0)
  java实现,动态规划算法。
  F(i)表示传入的String前i个字母能根据字典拆分,那么F(i)=true的条件是,存在j<i,能使从j+1到i之间的字母在字典中。
  比如对于题目中的用例,用judge数组来存boolean值。对于String="leetcode",刚开始,"l"不包含在集合["leet","code"]中,所以judge[1]=false,接下来,“le”也不......依次类推,当到"leet"时,这时“leet”在集合中,judge[4]=true,此时的judge数组为{t,f,f,f,t}接下来,看字母是否出自字典,就需要从第2个t处作为subString截取的起点。我想到的处理是,当发现截取字符串包含在集合内,就把judge置为true,并且把judge[begin]置为false,此时的judge数组变成了{f,f,f,f,t},那么下一次截取就是从这里的t开始的,第一次写出的程序如下:
package DynamicProgrammer.WordBreak;

import java.util.Set;

public class Solution {

    public boolean wordBreak(String s, Set<String> dict) {
            boolean[] judge = new boolean[100];
            judge[0] = true;
            int begin = 0;
            for (int i = 0; i < s.length(); i++) {
                for (int j = 0; j <= i; j++) {
                    if (judge[j] == true) begin = j;
                }
                String temp = s.substring(begin, i + 1);
                if (dict.contains(temp)) {
                    judge[i + 1] = true;
                    judge[begin] = false;
                } else judge[i] = false;
            }
            if (judge[s.length()] == true) return true;
            else return false;
        }
    }

  通过率为40%,不能通过Set=["aaa","aaaa"],String="aaaaaaa"用例。原因是,根据 上面的代码,该用例的judge数组开始是{t,f,f,f,...},begin=0,当i=3时,截取的temp="aaa",包含在Set中,这时judge数组变成{f,f,f,f,t},接下来,到i=5时,截取的temp=“aaa”,包含在Set中,这时judge数组变成{f,f,f,f,f,f,f,t},而再截取,最多剩2个a,不会再包含于Set,因此出错。
  出现上述结果,是因为Set集合中的元素本来就有包含关系,比如["aab""aabb"],“aabaaabb”也会出以上问题。而这类问题的共同点是:Set拼起来的字符串与String相同。因此修改代码,先判断一下这种情况,第二次提交的代码为;
package DynamicProgrammer.WordBreak;

import java.util.Set;

public class Solution {

    public boolean wordBreak(String s, Set<String> dict) {

        String string = "";
        Object[] objects = dict.toArray();
        for (int m = 0; m < objects.length; m++) {
            string = string + objects[m];
        }
        if (string.equals(s)) {
            return true;
        } else {


            boolean[] judge = new boolean[100];
            judge[0] = true;
            int begin = 0;
            for (int i = 0; i < s.length(); i++) {
                for (int j = 0; j <= i; j++) {
                    if (judge[j] == true) begin = j;
                }
                String temp = s.substring(begin, i + 1);
                if (dict.contains(temp)) {
                    judge[i + 1] = true;
                    judge[begin] = false;
                } else judge[i] = false;
            }
            if (judge[s.length()] == true) return true;
            else return false;
        }
    }

}
  通过率为90%,没通过的用例中Set的元素特别多,我想可能还是上面的问题,有包含关系的,导致错误,而不是简单地把Set中的元素拼接起来就能解决的。
  看来我的程序存在问题,改起来很麻烦。参考了别人的评论,当已有截取的字符串包含在字典中时,下一次截取需要改变起始点beginIndex,发现别人在处理时是在内层循环中:
for(int i=0;i<String.length;i++)
for(int j=0;j<i;j++)
   每次从0开始,当judge[j]和Set.contain(SubString(j,i))同时为true时,才给judge[i]赋true。以题目中的用例为例,当i=3时,judge数组为{t,f,f,f,t};i=7时,进入内层j循环,从0开始遍历,首先会遇到juge[0]=true,只有截取到“leet”时会包含在Set中,当j=4,judge[j]=true.截取到的SubString[4,8]是“code”,包含在Set中,那么会给judge[i]赋值true。
  两层循环中,i是从String开头到第i个字母是否在字典中的“i”。j是用来做judge赋值的判断用的,i先固定,每次看从j到i的字符串是否包含在Set中,而且当judge[j]本身也是true时,说明在j之前的字符串也包含在Set中,那么就可以给judge[j]赋值为true。
  最后,judge[String.length]==true时,返回true值。
  通过的代码如下:
import java.util.Set;

public class Solution {

    public boolean wordBreak(String s, Set<String> dict) {
      boolean[]judge=new boolean[s.length()+1];
      judge[0]=true;
for(int i=1;i<=s.length();i++)
    for(int j=0;j<i;j++)
    {if(judge[j]&&dict.contains(s.substring(j,i)))
    {judge[i]=true;  }}

        return judge[s.length()];
 }}


发表于 2019-08-13 20:54:17 回复(0)
import java.util.Set;
public class Solution {
    public boolean wordBreak(String s, Set<String> dict) {
         if(s.length()==0){
                return false;
            }
            if(dict.isEmpty()){
                return false;
            }
            boolean[] str=new boolean[s.length()+1];
            str[0]=true;
            for(int i=1;i<=s.length();i++){
                for(int j=0;j<i;j++){
                    if(str[j]&&dict.contains(s.substring(j,i))){
                        str[i]=true;
                        break;
                    }
                }
            }
            return str[s.length()];
        }
    }

发表于 2019-07-25 22:28:32 回复(0)
import java.util.*;

public class Solution {
    public boolean wordBreak(String s, Set<String> dict) {
        int oriLen = s.length();
       ArrayList<String> result = new ArrayList<>();
       if (oriLen == 0)
           return false;
       if (dict.size() == 0)
       {
           return false;
       }
       
       for (int i = 1;i <= oriLen; i++)
       {
           
           if (result.size() != 0)
           {
             for(int j = 0; j < result.size(); j++)
             {
                if (dict.contains(s.substring(result.get(j).replaceAll(" ","").length(), i)))
                {
                    StringBuilder sb = new StringBuilder();
                    sb.append(result.get(j));
                    sb.append(" ");
                    sb.append(s.substring(result.get(j).replaceAll(" ","").length(), i));
                    result.add(sb.toString());
                }
             }
           }
           if (dict.contains(s.substring(0,i)))
           {
               result.add(s.substring(0,i));
           }
       }
       
       
       while (result.size() != 0)
       {
            if (result.get(0).replaceAll(" ","").length() < oriLen)
                result.remove(0);
            else
                break;
       }
       
       if (result.size() == 0)
           return false;
       else
           return true;
    }
}

呵呵,不知道为什么就要报死循环,拿着测试样例在本地IDE上跑一点问题都没有
想直接把上一道题的思路移植过来,就这样。。
发表于 2019-07-02 14:47:23 回复(0)
private ArrayList<String> res = new ArrayList<>();
/**
 * 动态规划(我为什么要用动态规划。。题目要求的。。。)
 *
 * 状态转移方程:f(n) = f(i) + String(i+1,n)    0 <= i < n
 *
 * dp[i]存的是当前子串的开始索引,i是子串的结束索引
 * dp[i]链表长度为0代表不能被分割
 * 最后用递归获取所有可能的字符串
 *
 * 时间复杂度:O(n)+O(n^2)+O(m) m为结果个数
 */
public ArrayList<String> wordBreak(String s, Set<String> dict) {
    if (s == null || dict == null || dict.size() == 0) return res;
    if (s.equals("")) {
        if (dict.contains("")) {
            res.add("");
            return res;
        }
        return res;
    }
    ArrayList<ArrayList<Integer>> dp = new ArrayList<>();
    for (int i = 0; i <= s.length(); i++) {
        dp.add(new ArrayList<>());
    }
    dp.get(0).add(0);
    for (int i = 1; i <= s.length(); i++) {
        ArrayList<Integer> list = dp.get(i);
        for (int j = i - 1; j >= 0; j--) {
            ArrayList<Integer> listj = dp.get(j);
            String t = s.substring(j, i);
            if (listj.size() != 0 && dict.contains(t)) {
                list.add(j);
            }
        }
    }
    if (dp.get(s.length()).size() != 0) {
        StringBuilder sb = new StringBuilder();
        getResult(s, dp, sb, s.length());
    }
    return res;
}
private void getResult(String s, ArrayList<ArrayList<Integer>> dp, StringBuilder temp, int index) {
    if (index == 0) {
        res.add(temp.toString());
        return;
    }
    ArrayList<Integer> list = dp.get(index);
    for (int i = 0; i < list.size(); i++) {
        if (index == s.length()) {
            temp.append(s, list.get(i), index);
        } else {
            temp.insert(0, s.substring(list.get(i), index) + ' ');
        }
        getResult(s, dp, temp, list.get(i));
        if (index == s.length()) {
            temp.delete(0, index - list.get(i));
        } else {
            temp.delete(0, index - list.get(i) + 1);
        }
    }
}
发表于 2019-05-29 17:18:41 回复(0)

简单的 dp 解法:

import java.util.Set;

public class Solution {
    public boolean wordBreak(String s, Set<String> dict) {
        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++) {
                if (dp[j] && dict.contains(s.substring(j, i))) {
                    dp[i] = true;
                    break;
                }
            }
        }
        return dp[s.length()];
    }
}
发表于 2019-04-04 23:49:01 回复(0)
import java.util.HashSet;
import java.util.Set;
public class Solution {     /*      * f(i)表示s[0,i]是否可以分词      * 动态转移方程:f(i)=f(j) && f(j+1,i);0<=j<i      */     public boolean wordBreak(String s, Set<String> dict) {         boolean[] f=new boolean[s.length()+1];         f[0]=true;         for(int i=1;i<=s.length();i++){             for(int j=0;j<i;j++){                 if(f[j]&&dict.contains(s.substring(j,i))){                     f[i]=true;                     break;                 }             }         }         return f[s.length()];     }
}

发表于 2018-08-24 09:22:52 回复(0)
//有几个需要注意的边界点
import java.util.*;
public class Solution {
    public boolean wordBreak(String s, Set<String> dict) {
        int i,j;
        boolean array[]=new boolean[s.length()+1];//此处的Array大小
        array[0]=true;//此处的初始化
        for(i=1;i<array.length;i++) {
            for(j=0;j<i;j++) {
                if(array[j]&&dict.contains(s.substring(j, i))) {
                    array[i]=true;//此处的SubString
                    break;
                }
            }
        }
        return array[array.length-1];//此处的返回值
    
    }
}

发表于 2018-06-10 11:34:09 回复(1)
import java.util.*;

public class Solution {
    public boolean wordBreak(String s, Set<String> dict) {
        int len = s.length();
        boolean[] booleans = new boolean[len + 1];//从下标0开始i个字符是否可分
        booleans[0] = true;
        for (int i = 1; i <= len; i++) {//s[0..i-1]
            for (int j = i - 1; j >= 0; j--) {
                if (dict.contains(s.substring(j, i)) && booleans[j]) {
                    booleans[i] = true;
                    break;
                }
            }
        }
        return booleans[len];
    }
}

发表于 2018-04-29 10:46:38 回复(0)
import java.util.Set;
public class Solution {
    public boolean wordBreak(String s, Set<String> dict) {
        /*
        分析:
        动态规划:原问题可以划分为若干个子问题且子问题具有重叠性
        求解:保存已获取的子问题的解,用于求未解的子问题
        B[i]=true表示字符串s[0]~s[i-1]可以通过dict分解
        即前i个字符组成的字符串可以被字典分解
        初始时设B[0]=true,
        分解式:B[j]=B[i]&&dict.contains(sij-1),j由遍历dict得到
        (即遍历dict获取使得dict.contains(sij-1)为true的j),更新B[j]
        求B[n]即s[0]~s[n-1]是否可分解
        */
        boolean []B = new boolean[s.length()+1];//多出的一个存放初始B[0]
        B[0] = true;//初始值
        for(int i=0;i<s.length();i++){//每一次循环获取一个B[i]的值,最终得到B[n]
            if (B[i]){//B[i]=true,通过B[i]来确定下一个使得B[j]=true的j
                for(String sub:dict){//查找最小的i使得B[i]=true
                    int j = i+sub.length();
                    if (j<=s.length()){
                        String substr = s.substring(i,j);//取子串s[i,i+sub.length())
                        if (substr.equals(sub)){//B[j]=B[i]&&dict.contains(sij-1)
                            B[j] = true;
                        }
                    }
                }
            }//默认未置为true的B[i]为false
        }
        return B[s.length()];//最终返回结果B[n]
    }
}


编辑于 2018-04-14 16:03:49 回复(1)
import java.util.*;
public class Solution {
    public boolean wordBreak(String s, Set<String> dict) {
        int len = s.length();
        return wb(len,dict,s);
    }
    public boolean wb(int len, Set<String> dict, String s){
        if(len == 0)
            return true;
        for(int i = len-1; i >=0; i--){
            if(dict.contains(s.substring(i,len))){
                if(wb(i,dict,s))
                    return true;
            }
        }
        return false;
    }
}

发表于 2017-08-14 15:42:35 回复(0)
动态规划思想
import java.util.Set;
public class Solution {
    public boolean wordBreak(String s, Set<String> dict) {
        int len = s.length();
        // dp[i]表示0到i位可分
        boolean[] dp = new boolean [len+1];
        dp[0] = true;	//0到0可分
        for(int i = 1; i <= len; i ++){
            for(int j = 0; j < len; j ++){
                //如果 0-j可分,并且dic中包含j-i 的子串,那么0-i 为可分
                if(dp[j] && dict.contains(s.substring(j,i))){
                    dp[i] = true;
                }
            }
        }
        return dp[len];
    }
}

发表于 2017-08-08 16:43:24 回复(0)
import java.util.Set;
public class Solution {
    public boolean wordBreak(String s, Set<String> dict) {
        if(s == null || "".equals(s.trim()) || dict == null || dict.isEmpty()){
            return false;
        }
        boolean dp[] = new boolean[s.length() + 1];
        dp[0] = true;
        for(int i = 0; i < s.length(); i++) {
            for(int j = i; dp[i] && j < s.length(); j++){
                if (dict.contains(s.substring(i, j+1)))
                	dp[j+1]=true;
            }
        }
        return dp[s.length()];
    }
}

发表于 2017-07-11 07:42:41 回复(0)
import java.util.Set;

public class Solution {
    public boolean wordBreak(String s, Set<String> dict) {
        if (s == null || s.length() == 0 || dict == null || dict.size() == 0)
            return false;
        boolean[] isWords = new boolean[s.length() + 1];
        isWords[0] = true;
        for (int i = 0; i < s.length(); i++) {
            isWords[i + 1] = false;
        }
        for (int i = 0; i < s.length(); i++) {
            for (int j = 0; j < i + 1; j++) {
                if (isWords[j]) {
                    if (dict.contains(s.substring(j, i + 1))) {
                        isWords[i + 1] = true;
                        break;
                    }
                }
            }
        }
        return isWords[s.length()];
    }
}

发表于 2017-07-05 15:27:25 回复(0)
public class Solution { public boolean wordBreak(String s, Set<String> dict) { boolean[] f = new boolean[s.length() + 1];
        
        f[0] = true; /* First DP
        for(int i = 1; i <= s.length(); i++){
            for(String str: dict){
                if(str.length() <= i){
                    if(f[i - str.length()]){
                        if(s.substring(i-str.length(), i).equals(str)){
                            f[i] = true;
                            break;
                        }
                    }
                }
            }
        }*/ //Second DP for(int i=1; i <= s.length(); i++){ for(int j=0; j < i; j++){ if(f[j] && dict.contains(s.substring(j, i))){
                    f[i] = true; break;
                }
            }
        } return f[s.length()];
    }
}

发表于 2017-03-11 19:27:24 回复(0)