字典树应用及用哈希表代替

[LeetCode 30] Substring with Concatenation of All Words

题目

You are given a string s, and a list of words words, that are all of the same length. Find all starting indices of substring(s) in s that is a concatenation of each word in words exactly once and without any intervening characters.

测试案例

Input:
  s = "barfoothefoobarman",
  words = ["foo","bar"]
Output: [0,9]
Explanation: Substrings starting at index 0 and 9 are "barfoor" and "foobar" respectively.
The output order does not matter, returning [9,0] is fine too.

Input:
  s = "wordgoodstudentgoodword",
  words = ["word","student"]
Output: []

思路

假设字典长度为 num,单词长度为 strlength,字符串长度为 length。为了描述方便,记字典中的单词为 word,字符串中长度为 strlength 的子串为 str。

  1. 先将字典中的所有 word 放入字典树或者哈希表中(两者查询的时间复杂度相同,用哈希表的优点是:创建简单。字典树的创建太复杂)。key 为 word 本身,value 为 word 在字典中的下标。存在相同 word 时,只第一次放入,后续的 word 不放入,且不修改 value。

  2. 在放入的过程中,用数组记录字典中各 word 的重复次数,保存在 record[value]。value 为 word 在哈希表中的 value。

  3. 由于字典中 word 长度相同,可以将字符串分为 strlength 个序列。第 i 个序列含有 k 个 str:
    \[ s[i, i + strlength), \dots,s[i + (k - 1)strlength,i + kstrlength),\;//s[i,j)表示子串s.substring(i,j)\\ s.t.\;0 \leq i \lt strlength,\;i + kstrlength \leq length \]

  4. 查找 s 中所有的 concatenation 就是分别在每个序列中查找 concatenation 的问题。而在序列中查找 concatenation 就是判断序列中是否存在连续的 str 序列,各 str 的种类和个数均与字典中 word 对应相等。

  5. 通过查询哈希表,可以将序列中的每个 str 转变为其在字典中的下标,当 str 不在字典时,下标为0。因为字典中不同的 word 对应的 value 不相同。

  6. 从而问题转变为:给定一个整数序列 s 和一个整数数组 dict,在 s 找出所有的连续子列,满足子列中各数字的种类和个数和集合中的元素对应相同,不要求次序也对应相同。基于 record 数组,此问题可在线性时间求解。具体过程不写了。

代码如下

class Solution {5
    public List<Integer> findSubstring(String s, String[] words) {
        int num = words.length, length = s.length();
        List<Integer> res = new LinkedList<>();
        if(num == 0 || length == 0){
            return res;
        }
        int  strlen = words[0].length();
        //初始时为各word的个数,找到时减1,
        int[] record = new int[num]; 
        HashMap<String, Integer> dict = new HashMap<>(num * 4/3 + 1);
        //将每个 word 放入哈希表中
        for(int i = 0; i < num; i++){
            Integer temp;
            //查找是否存在相同 word,不存在时放入,value为word下标,更新record[i]
            if((temp = dict.get(words[i])) == null){
                dict.put(words[i], i);
                record[i]++;
            }
            //word 存在,从哈希表中取出word对应的value,更新record[value]
            else{
                record[temp]++;
            }
        }
        //统计当前队列中
        int count = 0;
        
        //把已匹配的word下标保存到队列,当 当前str的value 为0时,需要通过出队,清除之前的记录。
        Queue<Integer> queue = new LinkedList<>();
        //考虑 strlen 个序列
        for(int start = 0; start < strlen && start < length; start++){
            //循环开始时,queue 应为空队列,count = 0, record为空数组
            while(count > 0){
                count--;
                record[queue.poll()]++;
            }
            //考虑从i开始的str是否存在word匹配
            for(int i = start; i + strlen <= length; i += strlen){
                //查找str的value
                Integer index = dict.get(s.substring(i, i + strlen));
                //不匹配任何word,那么重置record,queue,count
                if(index == null){
                    while(count > 0){
                        count--;
                        record[queue.poll()]++;
                    }
                }
                //匹配的word已被匹配完,必须找到上一次匹配该字符串的位置,将其及其前面的匹配都清除掉。
                else if(record[index] == 0){
                    int temp;
                    while((temp = queue.poll()) != index){
                        count--;
                        record[temp]++;
                    }                                 
                    queue.offer(index);
                    if(count == num){
                        res.add(i + strlen - num * strlen);
                    }
                }
                //找到一个有效匹配
                else{
                    if(++count == num){
                        res.add(i + strlen - num * strlen);
                    }
                    record[index]--;
                    queue.offer(index);
                }
            }                  
        }
        return res;
    }
}

转载于:https://www.cnblogs.com/echie/p/9598569.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值