判断子序列
方法一:双指针
指针$i$指向$s$,指针$j$指向$t$,当s[i]==t[j]
时,两个指针同时后移,否则就将$j$后移,如果$j$已经到$t$的末尾而$i$还未到$s$的末尾,返回false,否则返回true。
1 | class Solution { |
2 | public boolean isSubsequence(String s, String t) { |
3 | if(s.length() == 0){ |
4 | return true; |
5 | } |
6 | int tindex = 0; |
7 | int sindex = 0; |
8 | while(tindex < t.length()){ |
9 | if(s.charAt(sindex) == t.charAt(tindex)){ |
10 | sindex++; |
11 | tindex++; |
12 | }else{ |
13 | tindex++; |
14 | } |
15 | if(sindex == s.length()){ |
16 | return true; |
17 | } |
18 | } |
19 | return false; |
20 | |
21 | } |
22 | } |
复杂度分析:
- 时间复杂度:$O(m+n)$,$m$是$t$的长度,$n$是$s$的长度。
- 空间复杂度:$O(1)$。
方法二:动态规划
如果是后续挑战中的情况,那么每次来一个序列$s$,就要重新比对,时间主要浪费在查找序列$t$上,我们可以用动态规划预处理一下$t$。令$dp[i][j]$表示从位置$i$开始字符$j$第一次出现的位置,那么转移方程为:
- 如果$t[i] == j$,那么$dp[i][j]=i$。
- 如果$t[i] \not= j$,那么$dp[i][j]=dp[i+1][j]$。
需要从后往前进行转移,边界条件是$dp[m][…]=m$,表示字符在位置$m$开始不存在。
1 | class Solution { |
2 | public boolean isSubsequence(String s, String t) { |
3 | int m = t.length(); |
4 | int n = s.length(); |
5 | int k = 'z' - 'a'; |
6 | int[][] dp = new int[m+1][k]; |
7 | for(int i=0; i<k; i++){ |
8 | dp[m][i] = m; |
9 | } |
10 | for(int i=m-1; i>=0; i--){ |
11 | for(int j=0; j<k; j++){ |
12 | if(t.charAt(i) == j + 'a'){ |
13 | dp[i][j] = i; |
14 | }else{ |
15 | dp[i][j] = dp[i+1][j]; |
16 | } |
17 | } |
18 | } |
19 | int index = 0; |
20 | for(int i=0; i<s.length(); i++){ |
21 | if(dp[index][s.charAt(i)-'a'] == m ){ |
22 | return false; |
23 | } |
24 | index = dp[index][s.charAt(i)-'a'] + 1; |
25 | } |
26 | return true; |
27 | } |
28 | } |
复杂度分析:
- 时间复杂度:$O(m \times |\sum|+n)$,$\sum$是字符集。
- 空间复杂度:$O(m \times |\sum|)$。