二分查找高效判定子序列 :: labuladong的算法小抄 #1037
Replies: 21 comments 8 replies
-
预处理的时间是O(N)吧。 |
Beta Was this translation helpful? Give feedback.
-
可以用HashMap做。把一系列字符串的第一个字符加入HashMap并且记录是哪个字符串。然后遍历t,对于匹配到的字符串,把它们的下一个字符加入HashMap直到末尾。时间复杂度是O(M*N)。M是字符串数量,N是t的长度。 |
Beta Was this translation helpful? Give feedback.
-
哦。时间复杂度好像不对…… |
Beta Was this translation helpful? Give feedback.
-
防止大家对这边的 index[c] 这种表达形式转不过弯来, 这边使用HashMap实现了一下 class Solution {
public boolean isSubsequence(String s, String t) {
TreeMap<Character,LinkedList<Integer>> map = new TreeMap<>();//存储t中所有 与s中字符相同的字符的索引位置
// char - list[字符索引]
for(int i = 0; i<t.length();i++){
LinkedList<Integer> list = new LinkedList<>();
if(map.containsKey(t.charAt(i))){//如果存在 直接在list末尾添加
map.get(t.charAt(i)).add(i);
}else{
list.addLast(i);//如果不存在需要创建一个键值映射
map.put(t.charAt(i),list);
}
}
int j = 0;//t中的游标位置
for(int i = 0; i< s.length();i++){
char c = s.charAt(i);// 当前s中需要匹配的字符
if(!map.containsKey(c)){//t中没有这个字符 直接返回false
return false;
}
//如果存在,找到比当前游标位置 大的 最小的索引
int pos = leftBound(map.get(c),j);
// System.out.println(pos); debug位置
// System.out.println("---");
if(pos == map.get(c).size()) return false;
j = map.get(c).get(pos) +1; //t上的指针
// System.out.println(j);debug 位置
}
return true;
}
//寻找s中字符 在t中字符索引 的 左边界
//list中存储的是 s中c字符对应的 所有索引 从小到大排序
//我们要找的是list中比 当前t上指针 j/tar 大的那个索引 直接跳过去
//这里得出的 左边界是 存储 对应字符索引的 list 的索引 所以,如果想要得到 对应t上的实际位置 还需要使用get(pos)
private int leftBound(LinkedList<Integer> list,int j){
int left = 0;
int right = list.size();
while(left < right){
int mid = (right - left)/2 + left;
if(j>list.get(mid)){
left = mid+1;
}else{
right = mid;
}
}
return left;
}
} |
Beta Was this translation helpful? Give feedback.
-
还可以用动态规划做 |
Beta Was this translation helpful? Give feedback.
-
这题也可以归并到LCS的问题 |
Beta Was this translation helpful? Give feedback.
-
贴个C++的用哈希表的代码 class Solution {
public:
bool isSubsequence(string s, string t) {
// 二分查找
int m = s.size(), n = t.size();
// 将较长的字符串保存到map里去
unordered_map<char,vector<int>> map_t(256);
for(int i = 0; i < n; i++){
map_t[t[i]].push_back(i);
}
// a 0
// h 1
// b 2
// g 3
// d 4
// c 5
// 串t指针
int j = 0;
// 借助map查找s[i]
for(int i = 0; i < m; i++){
if(!map_t.count(s[i])){
// s[i]不在t里,直接返回
return false;
}
// 如果存在,在map[s[i]]中搜索比j大的最小索引即可:即二分搜索左侧边界的结果值
int pos = leftBound(map_t[s[i]], j); // 传入的是s[i]在t中对应出现的所有下标索引
if(pos == map_t[s[i]].size()){
// 二分搜索区间中没有找到s[i]
return false;
}
j = map_t[s[i]][pos] + 1; // j 移动到二分搜索返回的pos处 后一位
}
return true;
}
int leftBound(vector<int>& vec_t, int j){
// 二分查找 搜索左侧边界 搜索j
int left = 0, right = vec_t.size();
// 左闭右开
while(left < right){
int mid = left + (right - left)/2;
if(vec_t[mid] < j){
left = mid + 1;
}else if(vec_t[mid] >j){
right = mid;
}else{
right = mid;
}
}
return left;
}
}; |
Beta Was this translation helpful? Give feedback.
-
跟着东哥刷完了,每道题都手敲了一遍,祝各位秋招好运 |
Beta Was this translation helpful? Give feedback.
-
打卡一刷结束 秋招加油 |
Beta Was this translation helpful? Give feedback.
-
792 题「 匹配子序列的单词数」 python version from collections import defaultdict
class Solution:
def numMatchingSubseq(self, s: str, words: List[str]) -> int:
char2idx = defaultdict(list)
for i, c in enumerate(s):
char2idx[c].append(i)
def left_bound(arr, target):
left, right = 0, len(arr)
while left < right:
mid = left + (right - left) // 2
if arr[mid] < target:
left = mid + 1
else:
right = mid
if left == len(arr):
return -1
return left
res = 0 # 记录有几个是子序列
for word in words:
i, j = 0, 0
for c in word:
idxs = char2idx.get(c)
if not idxs:
break
pos = left_bound(idxs, j)
if pos == -1:
break
j = idxs[pos] + 1
i += 1
if i == len(word):
res += 1
return res |
Beta Was this translation helpful? Give feedback.
-
typescript 一刷结束了 感谢东哥, 接下来用c++二刷,中途顺便参加下打卡活动。 大家加油! |
Beta Was this translation helpful? Give feedback.
-
392 利用字符串查找函数也可以实现【C语言】 bool isSubsequence(char * s, char * t){
int len = strlen(s);
char *p = t;
for (int i = 0; i < len; i++) {
p = strchr(p, s[i]);
if (p == NULL) {
return 0;
}
p++;
}
return 1;
} |
Beta Was this translation helpful? Give feedback.
-
792 匹配子序列的单词数 ,循环遍历一下392即可【C语言】 // 判断一个字符串是否是另一个字符串的子序列
bool IsSubSeq(char *words, char *s)
{
char *p = s;
int len = strlen(words);
for (int i = 0; i < len; i++) {
p = strchr(p, words[i]);
if (p == NULL) {
return 0;
}
p += 1;
}
return 1;
}
int numMatchingSubseq(char * s, char ** words, int wordsSize){
int num = 0;
for (int i = 0; i < wordsSize; i++) {
if (IsSubSeq(words[i], s)) {
num++;
}
}
return num;
} |
Beta Was this translation helpful? Give feedback.
-
肝了一个月,跟着刷完一遍了,刚刚入门,但是记得不深刻,还得再来一两遍,加油 |
Beta Was this translation helpful? Give feedback.
-
为啥代码过不去啊 |
Beta Was this translation helpful? Give feedback.
-
int numMatchingSubseq(String s, String[] words) {
int res = 0;
// 对 s(母串) 进行预处理
// char -> 该 char 的索引列表
ArrayList<Integer>[] index = new ArrayList[256];
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (index[c] == null) { // 这里c自动类型转换为ASC码
index[c] = new ArrayList<>();
}
index[c].add(i); // 实现了类似hashmap的效果。
}
// todo 遍历数组中的每个单词。
for (String word : words) {
// 指向s
int i = 0;
// 指向Word
int j = 0;
// 遍历每个字符串
while (j < word.length()) {
char c = word.charAt(j);
// todo 母串有这个字符,但是这个字符可能出现在母串的前面或者后面的任意位置。
// 所以这里我们搜索母串这个字符的索引list,查看当前i位置有没有被记录,
// 如果记录了更好,没记录就直接找到最近的一个记录位置。(至少说明是匹配上了,所以我们可以直接跳转到最近一个记录位置的下个位置)
int pos = left_bound(index[c], i);
if (pos == -1) {
break;
}
// 至少说明是匹配上了,所以我们可以直接跳转到最近一个记录位置的下个位置。
i = index[c].get(pos) + 1;
// 移动单词上的指针,准备取出下一个字符和母串匹配。
j++;
}
// 如果 word 完成匹配,则是子序列
if (j == word.length()) {
res++;
}
}
return res;
}
int left_bound(ArrayList<Integer> arr, int target) {
if (arr == null // 说明母串中没有这个字符。
|| arr.size() == 0 // 习惯而已,这里其实没有必要写,相当于也是说明母串中没有这个字符。
// || target<arr.get(0)
|| target > arr.get(arr.size() - 1)) {
return -1;
}
int left = 0;
int right = arr.size();
while (left < right) {
int mid = left + (right - left) / 2;
if (target == arr.get(mid)) {
right = mid;
} else if (target > arr.get(mid)) {
left = mid + 1;
} else if (target < arr.get(mid)) {
right = mid;
}
}
return left;
} 说一个比较有意思的地方,通常我在使用二分搜索时,会把4个合法性校验放在第一步,或者放在调用二分搜索的主函数的第一步。但是这里突然发现 // || target<arr.get(0)不能加上,原因是因为调用target<arr.get(0),但是至少说明这个字符是存在的,只不过索引位置比较靠后。 |
Beta Was this translation helpful? Give feedback.
-
int m = s.length(), n = t.length(); ----> int m = s.length, n = t.length(); |
Beta Was this translation helpful? Give feedback.
-
题中提示只有小写字母
数组 index 只需要申请 123 大小即可(字符 ArrayList<Integer>[] index = new ArrayList[123]; |
Beta Was this translation helpful? Give feedback.
-
二分法又被你小子给玩出了花 |
Beta Was this translation helpful? Give feedback.
-
没必要使用leftbound函数,普通的二分函数即可吧。 |
Beta Was this translation helpful? Give feedback.
-
文章链接点这里:二分查找高效判定子序列
评论礼仪 见这里,违者直接拉黑。
Beta Was this translation helpful? Give feedback.
All reactions