392. 判断子序列

 

 

题目要求s需要是t的子序列,子序列的定义是t中删除一些元素能够与s保持一致。

类似的题目有求2个字符串的最长公共子序列,经典的DP题。

不过本题我们可以偷懒使用双指针来处理,可以把题目简化为

s中的每一个字符是否都能在t中找到对应

时间O(m+n)(m、n分别为字符串s和t的长度),空间O(1)

 1     public boolean isSubsequence(String s, String t) {
 2         int i=0,j=0;
 3         while(i<s.length() && j<t.length()){
 4             if(s.charAt(i)==t.charAt(j)){
 5                 i++;
 6             }
 7             j++;
 8         }
 9         return i==s.length();
10     }

 

对于进阶问题,当我们在遇到超多字符串需要匹配时,如果我们

还使用上面方案的话,实际比较时间将会线性上升到k*(m+n)

于是我们可以参照KMP的next数组对字符串t做处理,

由于题目限定了s和t都只包含了小写字母,我们可以初始化一个

数组,存储第i个位置后出现字符j的位置,即,当我们遍历字符串

t到第i位时,此时s对应的字符是j,需要将t中i推进至哪一位。

这样做的好处是可以避免了s与t中字符不匹配,t中下标需要逐个递推

造成的时间浪费。

对于dp[i][j],我们发现只存在2种可能,(1)第i位便是j(2)第i位不是j

对于情况1,我们有dp[i][j]=i,对于情况2,我们便需要向后查找,于是有do[i][j]=dp[i+1][j]

由于第2种情况的存在,建立dp数组时我们需要从后向前递推

时间O(m*26+n*k)(建立dp数组耗时m*26,逐个匹配字符串s耗时k*s),空间O(m*26)

 1 public boolean isSubsequence(String s, String t) {
 2         int n=s.length(),m=t.length();
 3         int[][] dp=new int[m+1][26];
 4         
 5         // 初始化边界值,dp[i][j] = m表示t中不存在字符j
 6         for(int i=0;i<26;i++){
 7             dp[m][i] = m;
 8         }
 9 
10         for(int i=m-1;i>=0;i--){
11             for(int j=0;j<26;j++){
12                 // 从后向前递推,存在2种情况,t中第i个位置是j,那么
13                 // dp[i][j]=i,否则就要向后查找,于是有dp[i][j]=dp[i+1][j]
14                 if(t.charAt(i)==j+'a'){
15                     dp[i][j]=i;
16                 }else{
17                     dp[i][j]=dp[i+1][j];
18                 }
19             }
20         }
21 
22         int index=0;
23         for(int i=0;i<n;i++){
24             // 表示在t的剩余空间内没有再出现s中的字符,直接返回
25             if(dp[index][s.charAt(i)-'a']==m){
26                 return false;
27             }
28             // 成功匹配,则继续往后查找
29             index=dp[index][s.charAt(i)-'a']+1;
30         }
31 
32         return true;
33     }

 

posted @ 2021-05-08 11:52  jchen104  阅读(88)  评论(0编辑  收藏  举报