算法设计与分析第三周作业

算法设计与分析第三周作业

题目详情

本周所选题目:题目链接

题目大意为:给出一个字符串s和一个单词字符串集合words(单词长度全部一致),找出s中能在某个字符开始能够匹配words里所有单词的叠加(连接)的开始下标。

例子及解析如下:

1. 当输入为:

s = "barfoothefoobarman",
words = ["foo","bar"]

则因为从s的下标0处开始"bar","foo"分别对应了words的字符串,所以下标0被记录,

同理下标9开始的子字符串"foobar"也分别对应了words里的字符串,所以下标9倍记录。

即输出为[0, 9](用一个数组来表示)。

2. 当输入为

s = "abc",
words = ["d", "f"]

此时输出为一个空数组[]。


题目分析和算法的设计

如果使用一般的遍历方法(即对s和words的字符串的每个字符遍历)来求解的话,不仅时间复杂度不好,而且代码冗余。

所以不妨试一下使用words里的每个字符串来对s进行检索,如果发现有符合条件的s的子字符串,则记录开始下标。

算法详情:

(1)使用两个哈希表,一个用来存words里的所有字符串并以这些字符串为索引记录它们在words里的个数,另一个用来存匹匹配了的字符串的个数。

(2)算法涉及到两个循环,外循环为从头开始遍历s字符串,内循环为按照words里的每个字符串检索s字符串寻找与之匹配的子字符串。

(3)外循环退出条件为剩下的未遍历的字符个数小于words的全部字符串的总长度的值。因为剩下的子字符串的长度都已经小于words里所有字符串的连接而成的任一字符串的长度,显然,后面的肯定不会出现匹配的。

(4)内循环:

a. 以当前访问的s的字符开始取长度与words里字符串长度一致的s的子字符,如果该子字符串在words里,则以该子字符串为索引的哈希值加1,否则退出内循环。

b. 如果第二个哈希表的以该子字符串为索引的哈希值大于原有的words里的该字符串的个数,而内循环的次数为words的字符串的个数,这就说明此时一定存在一个words里的字符串没有出现在检索的s的子字符串中,即不匹配,此时退出内循环。

(5)如果上述内循环是因为遍历了words的所有字符串而退出循环的(即因为默认条件退出),此时由(4)可知,每个words里的字符串都被s中相邻的子字符串所匹配,即此时可记录此时对s进行遍历的开始的下标值。

(6)使用stl的map来作为哈希表,使用字符串函数substr(int startIndex, int length)来对s字符串取子字符串,该函数有两个参数,第一个参数为源字符串中开始截取的字符的下标,第二个参数为截取的字符串的长度。


代码详情

class Solution {
public:
    /*
      s = "barfoothefoobarman",
      words = ["foo","bar"]
    */
    vector<int> findSubstring(string s, vector<string>& words) {
        vector<int> ans;
        
        // 空串或者空集合
        if (words.empty() || s.empty()) return ans;

        int wordsL = words.size();
        int wordL = words[0].size();

        // 使用map进行哈希
        map<string, int> hash1;
        for (auto word : words) {
            hash1[word] ++;
        }

        // 若字符串中的字符长度小于一个单词的长度,即可退出循环
        int sL = (int)s.size();
        for (int i = 0; i <= sL - wordsL * wordL; i ++) {
            map<string, int> hash2;
            int j = 0;
            // 每次截取一个单词的长度的字符串出来比较
            for (; j < wordsL; j ++) {
                string test = s.substr(i + j * wordL, wordL);
                // 如果words里没有上面对应的字符串,则退出内循环, 否则把该单词放入hash2中
                if (hash1.find(test) == hash1.cend()) break;

                hash2[test] ++;

                // 哈希表2中的某一字符串的个数大于原有的,则不符合退出该循环
                if (hash2[test] > hash1[test]) break;
            }
            // 如果上述循环是由于j不满足条件退出的,说明此时能找到满足条件的字符串
            if (j == wordsL) ans.push_back(i);
        }
        return ans;
    }
};

思考与总结

此次作业主要涉及了如何合理使用哈希表(使用map构造)、重构算法思路(不需遍历源字符串和字符串集合里的字符串的每个字符),本次作业的难点也在如何重构算法思路上了,而算法中的两个判断是至关重要的,因为它们关系到之后能否在源字符串中找到相邻的匹配的子字符串。

还有就是外循环不需要遍历s的所有的字符,也是一种简单的优化吧。


参考资料:http://www.cnblogs.com/grandyang/p/4521224.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值