https://leetcode.cn/problems/is-subsequence/?envType=study-plan-v2&envId=top-interview-150
因为是子序列我们只要关心后一个字符在前一个字符后面出现过就行,至于在哪出现出现几次我们不关心,所以我们可以用HashMap<Character, ArrayList<Integer>> map;这个数据结构来存每一个字符出现过的位置然后遍历s,找到s中每一个字符在t中出现的位置(并且要在前一个字符的后面第一个,因为list是有序的我们可以通过二分来找),如果没找到则返回false,如果找到了则更新preIndex,继续遍历s,直到遍历完s,返回true.
题目后面那个拓展应该是让我们保留状态,我能想到比较好的方法就是构造字典树了,比如构造长度为i的字符串的字典树(1<i<=n),这样只用拿长度为i的字符串到对于长度的字典树匹配就行字典树匹配的时间是log n,总的时间就是nlog n,但是构造这样一个字典树组很麻烦,暂时只能想到这了。
class Solution {
public static void main(String[] args) {
String s = "abc";
String t = "ahbgdc";
System.out.println(new Solution().isSubsequence(s, t));
}
public boolean isSubsequence(String s, String t) {
int len_s = s.length(), len_t = t.length();
HashMap<Character, ArrayList<Integer>> map = new HashMap<>();//记录字符出现的位置
for(int i = 0; i < len_t; i++) {
if(!map.containsKey(t.charAt(i))) {
ArrayList<Integer> list = new ArrayList<>();
map.put(t.charAt(i), list);
}
map.get(t.charAt(i)).add(i);
}
int preIndex = -1, currIndex = 0;//preIndex记录上一个字符出现的位置,currIndex记录当前字符出现的位置
int index = 0;//当前在找的字符
while(index < len_s) {
if(!map.containsKey(s.charAt(index))) {//如果s中出现了t中没有的字符,则直接返回false
return false;
}
currIndex = getCurrIndex(preIndex, map.get(s.charAt(index)));//获取当前字符在t中出现的下一个位置
if(currIndex == -1) return false;//没找到
else {
preIndex = currIndex;//更新preIndex
}
index++;
}
return true;
}
//找当前字符在前一个字符下标后第一次出现的下标
public int getCurrIndex(int preIndex, ArrayList<Integer> list) {//通过二分找到一个出现在上一个字符后的待找字符
int left = 0, right = list.size() - 1;
while(left < right) {
int mid = (left + right) >> 1;
if(list.get(mid) > preIndex) right = mid;
else left = mid + 1;
}
return list.get(left) > preIndex ? list.get(left) : -1;
}
}