Leetcode 139&140. Word Break I & II

139. Word Break

题目

Given a non-empty string s and a dictionary wordDict containing a list of non-empty words, determine if s can be segmented into a space-separated sequence of one or more dictionary words. You may assume the dictionary does not contain duplicate words.

For example, given

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

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

题目解析

这题是求解能否分解成字典里的字符串,即是否问题,找到一个符合的即可。因此我们就开始搜索,想到了之前的回文串动态规划的表达式:f[n] = (f[i],s[i~n]) && f[i]=true。即,对于每个f[n]表示能否到达这个位置,遍历前面的位置,如果能到达前面的第i个位置,那我们就看看字典里是否有s[i~n]这个字符串,如果有,我们就可以通过这个字符到达当前字符,就可以确认可以到达i位置了,就不需要继续搜这个位置了。往下搜,能到最后一个位置说明能够分解。

代码

class Solution {
public:
    bool wordBreak(string s, vector<string>& wordDict) {
        unordered_set<string> ss;
        for (auto &i : wordDict)
            ss.insert(i);
        vector<bool> f(s.size()+1, false);
        f[0] = true;
        for (int i = 1; i <= s.size(); ++i)
        {
            for (int j = 0; j < i; ++j)
            {
                if (f[j])
                {
                    string subs = s.substr(j, i - j);
                    if (ss.count(subs))
                    {
                        f[i] = true;
                        break;
                    }
                }           
            }
        }
        return f[s.size()];
    }
};

140. Word Break II

题目

Given a non-empty string s and a dictionary wordDict containing a list of non-empty words, add spaces in s to construct a sentence where each word is a valid dictionary word. You may assume the dictionary does not contain duplicate words.
Return all such possible sentences.
For example, given

s = "catsanddog",
dict = ["cat", "cats", "and", "sand", "dog"].

A solution is ["cats and dog", "cat sand dog"].

题目解析

看到这个题目,发应过来又是一个深度搜索的问题,构建字符串。好吧,我们开始搜吧。

想法一

这是最直观的深度优先搜索,从起点出发,如果存在字典中字符串,移动一步,然后递归构造结果。over,果不其然超时了,卡在第29个测试用例上,人家可是hard类型的题目,这么简单的被做出来岂不是很没面子,继续想吧。

代码如下:

class Solution {
public:
    void solve(unordered_set<string> &ss,int idx,string s,vector<string> &vs,vector<string> &data)
    {
        if (idx == s.size())
        {
            string str;
            for (int i = 0; i < data.size() - 1; i++)
            {
                str += data[i];
                str += " ";
            }
            str += data[data.size() - 1];
            vs.push_back(str);
            return;
        }
        for (int i = idx; i < s.size(); ++i)
        {
            string tmp = s.substr(idx, i - idx + 1);
            if (ss.count(tmp))
            {
                data.push_back(tmp);
                solve(ss,i+1,s, vs, data);
                data.pop_back();
            }
        }
    }

    vector<string> wordBreak(string s, vector<string>& wordDict) {
        unordered_set<string> ss;
        for (auto &i : wordDict)
            ss.insert(i);
        vector<string> vs;
        vector<string> data;
        solve(ss, 0, s, vs, data);
        return vs;
    }
};

想法二

首先我们是不是递归的时候,遍历的太多,因为很多子串是不存在字典中,因此遍历是无效的。因此,我就想,我给每个位置所可能的后续点生成一个vector,每个位置只需要遍历这个数组就可以了,但是这个题目还是很顽强啊,还是超时==可能是生成这个vector花的时间更多吧。
代码如下:

class Solution {
public:
    void solve( unordered_map<int, vector<int>> &mii,int idx, string s, vector<string> &vs, vector<string> &data)
    {
        if (idx == s.size())
        {
            string str;
            for (int i = 0; i < data.size() - 1; i++)
            {
                str += data[i];
                str += " ";
            }
            str += data[data.size() - 1];
            vs.push_back(str);
            return;
        }
        vector<int> d = mii[idx];
        for (auto &i:d)
        {
            string tmp = s.substr(idx, i - idx + 1);
            data.push_back(tmp);
            solve(mii,i + 1, s, vs, data);
            data.pop_back();
        }
    }

    vector<string> wordBreak(string s, vector<string>& wordDict) {
        vector<string> vs;
        vector<string> data;
        unordered_set<string> ss;
        for (auto &i : wordDict)
            ss.insert(i);
        vector<bool> f(s.size() + 1, false);
        unordered_map<int, vector<int>> mii;
        f[0] = true;
        for (int i = 1; i <= s.size(); ++i)
        {
            for (int j = 0; j < i; ++j)
            {
                if (f[j])
                {
                    string subs = s.substr(j, i - j);
                    if (ss.count(subs))
                    {
                        f[i] = true;
                        mii[j].push_back(i - 1);                       
                    }
                }
            }
        }
        solve(mii,0, s, vs, data);
        return vs;
    }
};

想法三

哎,很丧气啊,怎么办,不会了。到底是哪里有重复计算了,想破脑壳,想起来前面的看能分解,只需要一个分解就可以了,那反过来,如果一个位置搜索不到结尾,那是不是以后到这个位置就直接不搜索了。对啊,这不就是备忘录方法吗!!!瞧这记性,做完上题忘下题。因此我们使用一个数组来记录每个地方能否到达结尾,一旦到达不了,做一个标记,下次搜到这个位置就不搜索了,好像是减少了不少。
代码如下:

class Solution {
public:
    bool solve(unordered_set<string> &ss, int idx, string s, vector<string> &vs, vector<string> &data, vector<bool> &f)
    {
        if (idx == s.size())
        {
            string str;
            for (int i = 0; i < data.size() - 1; i++)
            {
                str += data[i];
                str += " ";
            }
            str += data[data.size() - 1];
            vs.push_back(str);
            return true;
        }
        if (f[idx] == false)
            return false;
        bool flag = false;
        for (int i = idx; i < s.size(); ++i)
        {
            string tmp = s.substr(idx, i - idx + 1);
            if (ss.count(tmp))
            {           
                data.push_back(tmp);
                // 这里注意,要把solve写前面,因为或运算第一个是true就不算后面的
                flag = solve(ss, i + 1, s, vs, data, f) || flag;
                data.pop_back();
            }
        }
        f[idx] = flag;  
        return flag;
    }

    vector<string> wordBreak(string s, vector<string>& wordDict) {
        unordered_set<string> ss;
        for (auto &i : wordDict)
        {
            ss.insert(i);
        }       
        vector<string> vs;
        vector<string> data;
        vector<bool> f(s.size(), true);
        solve(ss, 0, s, vs, data,f);
        return vs;
    }
};

欢迎指正。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
本系统的研发具有重大的意义,在安全性方面,用户使用浏览器访问网站时,采用注册和密码等相关的保护措施,提高系统的可靠性,维护用户的个人信息和财产的安全。在方便性方面,促进了校园失物招领网站的信息化建设,极大的方便了相关的工作人员对校园失物招领网站信息进行管理。 本系统主要通过使用Java语言编码设计系统功能,MySQL数据库管理数据,AJAX技术设计简洁的、友好的网址页面,然后在IDEA开发平台中,编写相关的Java代码文件,接着通过连接语言完成与数据库的搭建工作,再通过平台提供的Tomcat插件完成信息的交互,最后在浏览器中打开系统网址便可使用本系统。本系统的使用角色可以被分为用户和管理员,用户具有注册、查看信息、留言信息等功能,管理员具有修改用户信息,发布寻物启事等功能。 管理员可以选择任一浏览器打开网址,输入信息无误后,以管理员的身份行使相关的管理权限。管理员可以通过选择失物招领管理,管理相关的失物招领信息记录,比如进行查看失物招领信息标题,修改失物招领信息来源等操作。管理员可以通过选择公告管理,管理相关的公告信息记录,比如进行查看公告详情,删除错误的公告信息,发布公告等操作。管理员可以通过选择公告类型管理,管理相关的公告类型信息,比如查看所有公告类型,删除无用公告类型,修改公告类型,添加公告类型等操作。寻物启事管理页面,此页面提供给管理员的功能有:新增寻物启事,修改寻物启事,删除寻物启事。物品类型管理页面,此页面提供给管理员的功能有:新增物品类型,修改物品类型,删除物品类型。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值