[leetcode] 30. 与所有单词相关联的字串(cn第653位做出此题的人~)

30. 与所有单词相关联的字串

这个题做了大概两个小时左右把。。。严重怀疑leetcode的judge机器有问题。同样的代码交出来不同的运行时长,能不能A题还得看运气?

大致思路是,给words生成一关于s的字典,用来记录每个word在s中出现的所有位置,注意可能会出现相同的word。然后递归枚举words的排列情况,一一校验是否符合条件(即连在一起)。用到了递归+记忆化搜索+kmp+几个剪枝

一直最后几个测试用例上TLE,囧啊,甚至一度怀疑是不是还有更优的做法。

然后开始考虑剪枝:

  1. 记忆化搜索:即同一层下同样的word只搜一次就好。(又多过了几个测试样例)
  2. 使用kmp算法:一开始在生成字典时直接用的java String的indexOf,效率太低了,换用kmp后,又多过了两个,不过还是不够
  3. 在确定头位置时,如果此时s剩余长度根本不够words的所有长度,直接跳过 ,妈的,此时发现还有最后两个测试用例过不了。。。仔细研究了下数据,又搞出一个特判剪枝2333333333
  4. 当words只有两种时,即word1与word2,那么可以先验证下word1+word2或word2+word1是否是s子串,如果不是的话不用搜了,肯定搜不到答案。。。
class Solution {

    class KMPStringMatcher {

        /**
         * 获取KMP算法中pattern字符串对应的next数组
         *
         * @param p 模式字符串对应的字符数组
         * @return
         */
        protected int[] getNext(char[] p) {
            // 已知next[j] = k,利用递归的思想求出next[j+1]的值
            // 如果已知next[j] = k,如何求出next[j+1]呢?具体算法如下:
            // 1. 如果p[j] = p[k], 则next[j+1] = next[k] + 1;
            // 2. 如果p[j] != p[k], 则令k=next[k],如果此时p[j]==p[k],则next[j+1]=k+1,
            // 如果不相等,则继续递归前缀索引,令 k=next[k],继续判断,直至k=-1(即k=next[0])或者p[j]=p[k]为止
            int pLen = p.length;
            int[] next = new int[pLen];
            int k = -1;
            int j = 0;
            next[0] = -1; // next数组中next[0]为-1
            while (j < pLen - 1) {
                if (k == -1 || p[j] == p[k]) {
                    k++;
                    j++;
                    next[j] = k;
                } else {
                    k = next[k];
                }
            }
            return next;
        }

        public int indexOf(String source, String pattern) {
            int i = 0, j = 0;
            char[] src = source.toCharArray();
            char[] ptn = pattern.toCharArray();
            int sLen = src.length;
            int pLen = ptn.length;
            int[] next = getNext(ptn);
            while (i < sLen && j < pLen) {
                // 如果j = -1,或者当前字符匹配成功(src[i] = ptn[j]),都让i++,j++
                if (j == -1 || src[i] == ptn[j]) {
                    i++;
                    j++;
                } else {
                    // 如果j!=-1且当前字符匹配失败,则令i不变,j=next[j],即让pattern模式串右移j-next[j]个单位
                    j = next[j];
                }
            }
            if (j == pLen)
                return i - j;
            return -1;
        }
    }

    public List<Integer> findSubstring(String s, String[] words) {
        List<Integer> ans = new ArrayList<>();
        if (s.equals("")) {
            return ans;
        }
        if (words.length == 0) {
            return ans;
        }

        Map<String, List<Integer>> dic = new HashMap<>();

        KMPStringMatcher kmpStringMatcher = new KMPStringMatcher();
        for (String word : words) {
            if (dic.containsKey(word)) {
                // word 可能出现重复的
                continue;
            }

            int k = 0, p = 0;

            int tmp = 0;
            while ((tmp = kmpStringMatcher.indexOf(s.substring(p), word)) != -1) {
                k = p + tmp;
                dic.computeIfAbsent(word, x -> new ArrayList<Integer>()).add(k);
                p = k + 1;
            }
        }

        Set<String> keys = dic.keySet();
        if (keys.size() == 2) {
            Iterator<String> iterator = keys.iterator();
            String tmp1 = iterator.next();
            String tmp2 = iterator.next();
            if (kmpStringMatcher.indexOf(s, tmp1 + tmp2) == -1 && kmpStringMatcher.indexOf(s, tmp2 + tmp1) == -1) {
                return new ArrayList<>();
            }
        }

        Map<String, Integer> flagMap = new HashMap<>();

        List<String> standBy = new ArrayList<>(Arrays.asList(words));
        for (String s1 : standBy) {
            if (!dic.containsKey(s1)) break;

            //记忆化搜索剪枝:同一层下同样的word没必要继续搜下去
            if (flagMap.containsKey(s1)) continue;
            flagMap.put(s1, 1);

            List<String> tmpStandBy = new ArrayList<>(standBy);
            tmpStandBy.remove(s1);
            for (Integer p : dic.get(s1)) {
                // 剪枝,如果字串长度根本不够匹配的,直接跳过
                if (s.length() - (p + s1.length()) < tmpStandBy.size() * s1.length()) continue;

                dfs(dic, tmpStandBy, p, p + s1.length(), s1.length(), ans);
            }
        }

//        return ans.stream().distinct().collect(Collectors.toList());
        return ans;
    }

    // p 当前子串的头位置,now表示当前子串的尾位置
    public boolean dfs(Map<String, List<Integer>> dic, List<String> standby, int p, int now, int len, List<Integer> ans) {
        if (standby.size() == 0) {
            ans.add(p);
            return true;
        }
        Map<String, Integer> flagMap = new HashMap<>();
        for (String s1 : standby) {
            if (!dic.containsKey(s1)) break;

            //记忆化搜索剪枝:同一层下同样的word没必要继续搜下去
            if (flagMap.containsKey(s1)) continue;
            flagMap.put(s1, 1);

            boolean flag = false;
            for (Integer position : dic.get(s1)) {
                if (position == now) {
                    flag = true;
                    break;
                }
            }
            if (flag) {
                List<String> tmpStandBy = new ArrayList<>(standby);
                tmpStandBy.remove(s1);
                if (dfs(dic, tmpStandBy, p, now + len, len, ans)) {
                    return true;
                }
            }
        }
        return false;
    }
}

另外,看别人题解貌似可以用滑动窗口的思路来做,回头研究下

原文地址:https://www.cnblogs.com/acbingo/p/9270877.html

时间: 2024-11-08 20:14:22

[leetcode] 30. 与所有单词相关联的字串(cn第653位做出此题的人~)的相关文章

Leetcode——30.与所有单词相关联的字串【##】

@author: ZZQ @software: PyCharm @file: leetcode30_findSubstring.py @time: 2018/11/20 19:14 题目要求: 给定一个字符串 s 和一些长度相同的单词 words.在 s 中找出可以恰好串联 words 中所有单词的子串的起始位置. 注意子串要与 words 中的单词完全匹配,中间不能有其他字符,但不需要考虑 words 中单词串联的顺序. 示例 1: 输入: s = "barfoothefoobarman&qu

30. 与所有单词相关联的字串、java实现

题目描述: 给定一个字符串 s 和一些长度相同的单词 words.在 s 中找出可以恰好串联 words 中所有单词的子串的起始位置. 注意子串要与 words 中的单词完全匹配,中间不能有其他字符,但不需要考虑 words 中单词串联的顺序. 示例 1: 输入: s = "barfoothefoobarman", words = ["foo","bar"] 输出: [0,9] 解释: 从索引 0 和 9 开始的子串分别是 "barfo

[Swift]LeetCode30. 与所有单词相关联的字串 | 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. Example 1: Input

leetcode——30. 串联所有单词的子串

class Solution: def findSubstring(self, s: str, words): if s=='' or words==[]:return [] n=len(words) m=len(words[0]) r=[] i=0 while i<len(s)-m*n+1: for j in words: p=s[i:i+m*n] t=list(p[m*i:m*(i+1)] for i in range(n)) if t.count(j)!=words.count(j): i

[leetcode]3无重复字符的最长字串

本题思路较清楚,通过一个数组记录各个位置所能够引导的最长字符串的长度即可. 稍微有一点难度在于如何向前寻找时判断已经出现了重复字符串,我采用的思路为只有当前项的长度等于当前长度时,认为尚未出现重复字符串,并更新前项长度++. 1 int max=0; 2 int st[100000]={0};//表示以它为开头的最长不重复字串长度 3 int i=0; 4 char p; 5 while(s[i]!='\0') 6 { 7 p=s[i]; 8 st[i]=1; 9 max=st[i]; 10 f

创建Hive/hbase相关联的表异常

hive> CREATE TABLE hperson(id string, name string,email string) STORED BY 'org.apache.hadoop.hive.hbase.HBaseStorageHandler' WITH SERDEPROPERTIES ("hbase.columns.mapping" = ":id,cf1:name,cf2:email") TBLPROPERTIES ("hbase.table.

表单标签&lt;label&gt;----------------------用for属性 和radio 的id相关联 可以让鼠标点字就相当于点击radio

定义和用法 <label> 标签为 input 元素定义标注(标记). label 元素不会向用户呈现任何特殊效果.不过,它为鼠标用户改进了可用性.如果您在 label 元素内点击文本,就会触发此控件.就是说,当用户选择该标签时,浏览器就会自动将焦点转到和标签相关的表单控件上. <label> 标签的 for 属性应当与相关元素的 id 属性相同. <form><label for="male">Male</label><

Entity Framework 6 Recipes 2nd Edition(12-5)译 -&gt; 自动删除相关联实体

12-5. 自动删除相关联实体 问题 当一个实体被删除时,你想自动删除它相关联的实体 解决方案 假设你有一个表结构由一个course (科目), course 的classes (课程),以及enrollment (登记学生选课),如 Figure 12-5所示:. Figure 12-5. The Course, Class, and Enrollment tables in our database 由上述表生成一个模型,如下图Figure 12-6所示:. Figure 12-6. A m

已有打开的与此 Command 相关联的 DataReader,必须首先将它关闭。

多次用到SqlDataReader 要先关闭,再执行ExecuteNonQuery操作 每用一次需要先进行关闭,再执行新的一次,要不然就会报错“已有打开的与此 Command 相关联的 DataReader,必须首先将它关闭.” 那么如何解决呢? 方法有两种1,在ConnectionString加上MultipleActiveResultSets=true,但只适用于SQL 2005以后的版本2.选择读出SqlDataReader 中的数据给List或者arraylist之类的,之后进行关闭返回