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;
}
}

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

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

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

    @author: ZZQ @software: PyCharm @file: leetcode30_findSubstring.py @time: 2018/11/20 19:14 题目要求: 给定一 ...

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

    题目描述: 给定一个字符串 s 和一些长度相同的单词 words.在 s 中找出可以恰好串联 words 中所有单词的子串的起始位置. 注意子串要与 words 中的单词完全匹配,中间不能有其他字符, ...

  3. Leetcode 30.与所有单词相关联的子串

    与所有单词相关联的字串 给定一个字符串 s 和一些长度相同的单词 words.在 s 中找出可以恰好串联 words 中所有单词的子串的起始位置. 注意子串要与 words 中的单词完全匹配,中间不能 ...

  4. LeetCode(30):与所有单词相关联的字串

    Hard! 题目描述: 给定一个字符串 s 和一些长度相同的单词 words.在 s 中找出可以恰好串联 words 中所有单词的子串的起始位置. 注意子串要与 words 中的单词完全匹配,中间不能 ...

  5. [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 sta ...

  6. 030 Substring with Concatenation of All Words 与所有单词相关联的字串

    给定一个字符串 s 和一些长度相同的单词 words,找出 s 与 words 中所有单词(words 每个单词只出现一次)串联一起(words中组成串联串的单词的顺序随意)的字符串匹配的所有起始索引 ...

  7. Java实现 LeetCode 30 串联所有单词的子串

    30. 串联所有单词的子串 给定一个字符串 s 和一些长度相同的单词 words.找出 s 中恰好可以由 words 中所有单词串联形成的子串的起始位置. 注意子串要与 words 中的单词完全匹配, ...

  8. [LeetCode] 30. 串联所有单词的子串

    题目链接: https://leetcode-cn.com/problems/substring-with-concatenation-of-all-words/ 题目描述: 给定一个字符串 s 和一 ...

  9. Leetcode 30 串联所有单词的子串 滑动窗口+map

    见注释.滑动窗口还是好用. class Solution { public: vector<int> findSubstring(string s, vector<string> ...

随机推荐

  1. 2021 年最值得推荐的 7 个 Angular 前端组件库 - DevUI

    摘要:DevUI 是一款面向企业中后台产品的开源前端解决方案,它倡导沉浸.灵活.至简的设计价值观,提倡设计者为真实的需求服务,为多数人的设计,拒绝哗众取宠.取悦眼球的设计.如果你正在开发 ToB 的工 ...

  2. 【Spring】循环依赖

    @ 目录 循环依赖 是什么? Spring是如何解决的? 源码分析 细节 循环依赖 是什么? ​ 简单的来说就是对象a的属性中引用了对象b,对象b的属性中引用了对象c......最后引用到a. < ...

  3. elasticsearch jvm优化

    测试环境elasticsearch jvm 4G jdk1.8 [serveradm@test-log-server elasticsearch]$ java -version java versio ...

  4. Tomcat配置及网站创建教程(IDEA)

    Tomcat在本机的配置 解压 解压Tomcat压缩包后就算安装完成,解压完成生成文件夹 配置环境变量 1.配置JAVA_HOME 控制面板--系统--查看高级系统设置--环境变量--系统环境变量 新 ...

  5. hdu 5063 不错的小想法题(逆向处理操作)

    题意:       刚开始的时候给你一个序列,长度为n,分别为a[1]=1,a[2]=2,a[3]=3,a[4]=4...a[n]=n,然后有4种操作如下: Type1: O 1 call fun1( ...

  6. hdu4973 线段树(题目不错,用了点,段,更新查找还有DFS)

    题意:       给你一个初始序列,初始序列长度n,分别为1 2 3 4 5 ....n,有两种操作 (1)D l r 把l_r之间的数据都复制一遍 1 2 3 4 5 6 D 2 4 = 1 2 ...

  7. 关于 C/C++ 函数调用约定

    关于 C/C++ 函数调用约定,大多数时候并不会影响程序逻辑,但遇到跨语言编程时,了解一下还是有好处的. VC 中默认调用是 __cdecl 方式,Windows API 使用 __stdcall 调 ...

  8. 更好的滚动体验>better-scroll

    认识better-scroll better-scroll是一款重点用于解决移动端(已支持PC)各种滚动场景需求的插件,可使页面滚动效果更加流畅且富有弹性 better-scroll是用纯JavaSc ...

  9. Asp.NetCore Web应用程序中的请求管道和中间件

    你是否会迷惑当我们请求一个ASP.NetWeb应用程序以后,它是怎么处理这些请求的,后台是怎么工作的,今天就讲一下Asp.NetCore Web应用程序中的请求处理过程. 上一节,我们讲到,Start ...

  10. 【Mybatis源码解析】- 整体架构及原理

    整体架构 version-3.5.5 在深入了解Mybatis的源码之前,我们先了解一下Mybatis的整体架构和工作原理,这样有助于我们在阅读源码过程中了解思路和流程. 核心流程 在上一遍的入门程序 ...