leetcode 30. 串联所有单词的子串-java详细版本

题目所属分类

可以用字符串哈希做 这样的话复杂度会变成O(n)

原题链接

给定一个字符串 s 和一个字符串数组 words。 words 中所有字符串 长度相同。

s 中的 串联子串 是指一个包含 words 中所有字符串以任意顺序排列连接起来的子串。

例如,如果 words = [“ab”,“cd”,“ef”], 那么 “abcdef”, “abefcd”,“cdabef”, “cdefab”,“efabcd”, 和 “efcdab” 都是串联子串。 “acdbef” 不是串联子串,因为他不是任何 words 排列的连接。
返回所有串联字串在 s 中的开始索引。你可以以 任意顺序 返回答案。

代码案例:输入:s = “barfoothefoobarman”, words = [“foo”,“bar”]
输出:[0,9]
解释:因为 words.length == 2 同时 words[i].length == 3,连接的子字符串的长度必须为 6。
子串 “barfoo” 开始位置是 0。它是 words 中以 [“bar”,“foo”] 顺序排列的连接。
子串 “foobar” 开始位置是 9。它是 words 中以 [“foo”,“bar”] 顺序排列的连接。
输出顺序无关紧要。返回 [9,0] 也是可以的。

题解

  1. (重点)划分区间,因为每个word的长度是相同的,因此可以将s串划分为从第0个元素开始匹配、从第1个元素开始匹配、… 、从第w-1个元素开始匹配的w种情况,这样就能保证给定的words串不会横跨区间。
    在这里插入图片描述
  2. 在上述基础上,用一个哈希表a存储给定的m个单词;用另一个哈希表b存储滑动窗口中的元素,每次滑动窗口时,只会在此哈希表中插入一个元素,再删除一个元素;(键值对为 ---- 单词 :单词出现的次数)
  3. 用cnt存储哈希表b中有多少个单词是哈希表a中的。如果窗口中出现过该单词,并且窗口中该单词的数量小于原单词组中的数量,则表示该单词有效,cnt++。最后,如果cnt = m,则得到一个答案。从而使得滑动窗口匹配子串时的时间复杂度降为O(1)。
    这个写的好一些
    应用了字符串哈希
class Solution {
    public List<Integer> findSubstring(String s, String[] words) {
        // 存储答案
        List<Integer> list = new ArrayList<Integer>();
        // n为字符串长度,m为单词数量,w为单词长度
        int n = s.length(),m = words.length,w = words[0].length();
        // 将原来的单词存储在哈希表中,value值表示它有多少个
        HashMap<String, Integer> map_words = new HashMap<String, Integer>();
        for(String word : words) map_words.put(word,map_words.getOrDefault(word,0) + 1);
        // 定义当前窗口的哈希表
        HashMap<String, Integer> map_windows = new HashMap<String, Integer>();
        // 划分s串,定义窗口的起始位置从0 到 n - m * w
        for(int i = 0;i <= n - m * w;i ++)
        {
            // 每次读入窗口清空原来的窗口哈希表
            map_windows.clear();
            // cnt表示哈希表map_windows中有多少个单词是哈希表map_words中的
            int cnt = 0;
            // 划分单词m次
            for(int j = 0;j < m;j ++)
            {
                // 将长度为w的单词划分出来
                String t = s.substring(i + j * w,i + j * w + w);
                // 如果原单词中存在t,则继续,否则直接break此次循环
                if(map_words.containsKey(t))
                {
                    // 将t存储在map_windows中
                    map_windows.put(t,map_windows.getOrDefault(t,0) + 1);
                    // 如果t在窗口中出现的次数大于t在原单词组中出现的次数,直接break此次循环
                    if(map_windows.get(t) > map_words.get(t)) break;
                    else cnt++;
                }
                else break;
            }
            if(cnt == m) list.add(i);
        }
        return list;
    }
}

 

字符串哈希没用上

class Solution {
    public List<Integer> findSubstring(String s, String[] words) {
        List<Integer> res = new ArrayList<>();      
        if(words.length == 0) return res;
        int n = s.length();int m = words.length ; int w = words[0].length();
         HashMap<String, Integer> total = new HashMap<String, Integer>();
        for(String word : words){
            total.put(word,total.getOrDefault(word,0) + 1);
        }
        for(int i = 0 ; i < w  ;i++){
        // 因为每一个单词的长度是相等的 == w,所以只需要遍历起点 0--w - 1
            //当前窗口的的哈希表
        HashMap<String, Integer> find = new HashMap<String, Integer>();
        int cnt = 0 ;// 统计窗口内单词在 words 中出现的次数
        for(int j = i ; j + w <= n  ; j += w ){
             //窗口已经满,需要去掉窗口最左边的单词,才能在窗口中添加新的单词
            if(j >= i + m * w){
                String word = s.substring(j-m*w, w+j-m*w);
                find.put(word,find.get(word)-1);//去除窗口最左边的单词
                 // 不加 == 是因为没有减去的时候是 == ,减去了之后是 <,那 word 肯定是需要的单词
                 //total.get(word) != null这个不加会报错
                if (total.get(word) != null && find.get(word) < total.get(word))//说明减到的是一个有效单词
                        cnt--;
            }
             String word = s.substring(j, j+w); 
                find.put(word, find.getOrDefault(word, 0) + 1); //在窗口最右边添加新的单词
                if (total.get(word) != null && find.get(word) <= total.get(word))
                    cnt++;
                if (cnt == m)
                    res.add(j - (m-1)*w);//区间的起点
        }

        }
        return res ;
    }
}

总结

  • 划分字符串的技巧
  • 用哈希表存储单词以及单词出现的次数
  • 用cnt来控制两个哈希表是否相同,如果窗口中出现过该单词,并且窗口中该单词的数量小于原单词组中的数量,则表示该单词有效,cnt++。
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 可以使用Java中的排序功能来实现。可以使用Arrays.sort()函数,将列表中的元素按照字母顺序排序,或者使用Collections.sort()函数,将列表中的元素按用户指定的排序规则排序。 ### 回答2: 为了实现LeetCode 2561题(Rearranging Fruits)的要求,需要使用Java编程语言。主要思路是遍历给定的水果数组,同时用一个哈希表来记录每个水果出现的次数。然后根据题目要求,重新排列水果使得相同类型的水果尽可能接近,并且按照出现次数的非递增顺序排序。 具体实现步骤如下: 1. 创建一个HashMap来存储每个水果的出现次数。遍历给定的水果数组,如果该水果已经存在于HashMap中,则将其出现次数加1;否则,将该水果添加到HashMap,并将其出现次数初始化为1。 2. 创建一个ArrayList来存储已经排列好的水果。通过HashMap的entrySet方法获取到每种水果和它的出现次数,然后将这些entry按照出现次数的非递增顺序进行排序。 3. 遍历排序好的entry集合,根据每个水果的出现次数,在ArrayList中连续添加相应数量的水果。 4. 返回排列好的水果数组。 以下是Java代码的示例实现: ```java import java.util.*; class Solution { public String[] rearrange(String[] fruits) { HashMap<String, Integer> fruitCountMap = new HashMap<>(); // 统计每个水果的出现次数 for (String fruit : fruits) { if (fruitCountMap.containsKey(fruit)) { fruitCountMap.put(fruit, fruitCountMap.get(fruit) + 1); } else { fruitCountMap.put(fruit, 1); } } ArrayList<Map.Entry<String, Integer>> sortedEntries = new ArrayList<>(fruitCountMap.entrySet()); // 根据出现次数进行非递增排序 Collections.sort(sortedEntries, new Comparator<Map.Entry<String, Integer>>() { public int compare(Map.Entry<String, Integer> entry1, Map.Entry<String, Integer> entry2) { return entry2.getValue().compareTo(entry1.getValue()); } }); ArrayList<String> rearrangedFruits = new ArrayList<>(); // 根据出现次数连续添加水果 for (Map.Entry<String, Integer> entry : sortedEntries) { String fruit = entry.getKey(); int count = entry.getValue(); for (int i = 0; i < count; i++) { rearrangedFruits.add(fruit); } } return rearrangedFruits.toArray(new String[0]); } } ``` 使用以上代码,可以对给定的水果数组进行重新排列,使得相同类型的水果尽可能接近,并且按照出现次数的非递增顺序进行排序。返回的结果就是排列好的水果数组。 ### 回答3: 题目要求将一个字符串中的水果按照特定规则重新排列。我们可以使用Java来实现这个问题。 首先,我们需要定义一个函数来解决这个问题。 ```java public static String rearrangeFruits(String fruits) { // 将字符串转换为字符数组方便处理 char[] fruitArray = fruits.toCharArray(); // 统计每种水果的数量 int[] fruitCount = new int[26]; for (char fruit : fruitArray) { fruitCount[fruit - 'a']++; } // 创建一个新的字符数组来存储重新排列后的结果 char[] rearrangedFruitArray = new char[fruitArray.length]; // 逐个将水果按照规则放入新数组中 int index = 0; for (int i = 0; i < 26; i++) { while (fruitCount[i] > 0) { rearrangedFruitArray[index++] = (char) ('a' + i); fruitCount[i]--; } } // 将字符数组转换为字符串并返回 return new String(rearrangedFruitArray); } ``` 上述代码中,我们首先将字符串转换为字符数组,并使用一个长度为26的数组来统计每一种水果的数量。然后,我们创建一个新的字符数组来存储重新排列后的结果。 接下来,我们利用双重循环将每一种水果按照规则放入新数组中。最后,我们将字符数组转换为字符串并返回。 例如,如果输入字符串为`"acbba"`,则经过重新排列后,输出结果为`"aabbc"`。 这样,我们就用Java实现了题目要求的功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

依嘫_吃代码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值