动态规划字符串拆分java_动态规划的思想来求解字符串分割问题

Given a string s and a dictionary of words dict, determine if s can be segmented into a space-separated sequence of one or more dictionary words.

For example, given

s = "leetcode",

dict = ["leet", "code"].

Return true because "leetcode" can be segmented as "leet code".

0,问题介绍

给定一个包含了若干单词的字典以及一个字符串,将该字符串分割,分割后的得到的单词必须由字典中的单词组成。

1,动态规划思想的介绍---参照《算法导论》中关于动态规划的分析来分析此问题

①最优子结构

要判断整个字符串 s[0..length] 能否被分割,可先判断 s[0...length -1] 能否被分割;而判断 s[0...length -1] 能否被分割,可先判 s[0...length-2] 能否被分割,……直至判断 s[0] 能否被分割,而 s[0] 能否被分割是显而易见的---(查找 s[0] 在不在字典中即可--dict.contains(s[0])???)

递归表达式如下:

d82b98f9c1a7d46122ca298c1994fe06.png

②子问题的总个数是多少?每个子问题可以有多少种选择?算法的时间复杂度是多少?

子问题的个数一个有 n 个,n为字符串的长度。每个子问题有 2 种选择,即要么可以分割,要么不可以分割。从这个角度来看,算法的时间复杂度为 O(n)。但是,这里没有考虑每个子问题做选择时,需要执行多少步骤,代价是多大?从下面代码的第 18 行 for 循环中可以看出,第 i 个子问题 需要循环 i 次,那么时间复杂度为 1+2+3+……+n = O(n^2)。

③用到了 动态规划中的重叠子问题

动态规划中的重叠子问题是指,在将原问题分解的过程中,得到了若干子问题,再将这些子问题分解时,又得到若干更小子问题……,这些子问题中,有很多是重复的。这个特点与分治算法分解问题时不同,分治算法分解得到的子问题一般是独立的,各个子问题之间没有太多联系。基于动态规划子问题的重复性,因此,在求解出某个子问题之后,将它的结果记录下来,当下一次再碰到此问题时,直接查找它的结果,而不是再一次计算该子问题。

在 wordBreak 问题中的第2点动态规划求解思路分析(下面 第2点)中,求解 match[i] 的值可能需要用到 match[i-1]、match[i-2]、……match[0]的值;

求解match[i-1]的值 可能需要用到 match[i-2]、match[i-3]、match[0]。match[i] 即对应 s[0..i-1]能否分割这个问题,再结合动态规划自底向上求解问题的性质,把"底层"问题的求解结果记录下来,在求解到“上层”问题时,查找“底层”问题的结果,这种方式可以提高算法的运行效率。这也是《算法导论》中求LCS问题中提到的“查表”。具体的代码体现如下:求mathc[i]的值时,需要查找match[j]的值是多少。

for (int i = 1; i < length + 1; i++) {for (int j = 0; j < i; j++) {if (match[j] &&wordDict.contains(s.substring(j, i))) {

match[i]= true;

2,用动态规划求解的思路

①match[s.length]  用来表示字符串 s[0...length-1]  每部分能否分割。初始时,match[0]=true; match[i] 表示 s[0...i-1] 这段字符能否分割。

match[s.length] 则表示整个字符串(s[0...length-1])能否分割。

②若match[i]=true,表示 s[0...i-1]能够分割,则需要满足以下条件中的任一 一个:

a)match[0]==true && s[0...i-1] 在字典中;b)match[1] == true && s[1...i-1] 在字典中;c)match[2] == true && s[2...i-1]在字典中;.....

d)match[i-1]==true && s[i-1] 在字典中。 s[i...j]在字典中表示:字符串s中由下标为 i 到 j 的子字符串是字典中的某个单词。

具体分析:设 s = "aaaaa",dict = ["aaaa","aaa"]

由于 "a" 不在dict中,故match[1] = false; "aa" 不在dict中 且 (match[1]=false && "a" 不在dict中),故match[2]=false,对于 match[3]的值,

先判断 "aaa" 是否在dict中,由于 "aaa"在dict 中,故 match[3] = true,对于match[4],由于"aaaa"在dict中故 match[4] = true,

对于 match[5],先判断 "aaaaa",由于它不属于dict ;再继续判断,(match[1] = true?) and (s[1...5] exist in dict?),虽然,s[1...5]="aaaa" exist in dict 为true,但是 match[1] = false,故此时还不能判断match[5];

将 "aaaaa" 先分成 "a" ,再分成 "aaaa"是否可以?

由于"a" 不在 dict中 因为,match[1] == false尽管 "aaaa" 在 dict 中,但还是故不能这样分,因为 "a" 不在 dict 中

故match[2] 赋值为 false

再继续判断,(match[2] = true?) and (s[2...5] exist in dict?)....

将 "aaaaa" 先分成 "aa",再分成 "aaa",是否可以?

由于"aa" 不在 dict 中,因为判断match[2] == false.

尽管"aaa"在dict 中,但还是不能这样分

故match[3] 赋值为 false

直至判断到

match[4]==true? and s[4...4] == "a" exist in dict?? 此时,尽管match[4] == true 但是 s[4]== "a" 为 false,故match[5]= false.

即对于 设 s = "aaaaa",dict = ["aaaa","aaa"],程序将返回false.

3,完整代码如下:

1 importjava.util.HashSet;2 importjava.util.Set;3

4 public classSolution {5

6 public boolean wordBreak(String s, SetwordDict) {7 //参数校验

8 if (s == null || s.length() < 1 || wordDict == null || wordDict.size() < 1) {9 return false;10 }11

12 //标记是否匹配,match[i]表示 str[0, i-1]可以分割

13 int length =s.length();14 boolean[] match = new boolean[length + 1];15 match[0] = true;16

17 for (int i = 1; i < length + 1; i++) {18 for (int j = 0; j < i; j++) {19 if (match[j] &&wordDict.contains(s.substring(j, i))) {20 //s(0,n) = s(0,i) + s(i,j) + s(j,n)

21 match[i] = true;22 break;23 }24 }25 }26 returnmatch[length];27 }28 public static voidmain(String[] args) {29 Solution s = newSolution();30 Set set = new HashSet();31 set.add("aaaa");32 set.add("aaa");33 boolean result = s.wordBreak("aaaaa", set);34 System.out.println(result);35 }36

37 }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值