二分查找高效判定子序列 :: 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.
-
打卡一刷结束 秋招加油 |
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.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
文章链接点这里:二分查找高效判定子序列
评论礼仪 见这里,违者直接拉黑。
Beta Was this translation helpful? Give feedback.
All reactions