LeetCode392. 判断子序列 动态规划解法(带图)


双指针法比较简单,也就不过多介绍了,不过官方题解的动态规划作法,,作为一个新手,觉得十分的头大, 花了很久也算是搞明白了到底是怎么的一个运作过程, 我会努力写qwq,希望能帮助看到这篇文章的读者理解算法的含义。

我会画几个图。。不会画动态图。。我就举两个例子 ,应该就能很好地理解啦。

(可以结合图片自行debug一下,我就是用了笨办法,画了个demo的表格,然后自己debug才看明白的。。)

1. 题目

给定字符串 s 和 t ,判断 s 是否为 t 的子序列。

你可以认为 s 和 t 中仅包含英文小写字母。字符串 t 可能会很长(长度 ~= 500,000),而 s 是个短字符串(长度 <=100)。

字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"是"abcde"的一个子序列,而"aec"不是)。

示例 1:
s = "abc", t = "ahbgdc"

返回 true.

示例 2:
s = "axc", t = "ahbgdc"

返回 false.

后续挑战 :

如果有大量输入的 S,称作S1, S2, ... , Sk 其中 k >= 10亿,你需要依次检查它们是否为 T 的子序列。在这种情况下,你会怎样改变代码?

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/is-subsequence
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

2. 代码

class Solution {
    public boolean isSubsequence(String s, String t) {
        int n = s.length(), m = t.length();

        int[][] f = new int[m + 1][26];
        for (int i = 0; i < 26; i++) {
            f[m][i] = m;
        }

        for (int i = m - 1; i >= 0; i--) {
            for (int j = 0; j < 26; j++) {
                if (t.charAt(i) == j + 'a')
                    f[i][j] = i;
                else
                    f[i][j] = f[i + 1][j];
            }
        }
        int add = 0;
        for (int i = 0; i < n; i++) {
            if (f[add][s.charAt(i) - 'a'] == m) {
                return false;
            }
            add = f[add][s.charAt(i) - 'a'] + 1;
        }
        return true;
    }
}

3. 思路

Demo1: “ahbgdc” 中寻找 “abc”

这个填写的顺序从下往上 从左往右, 不知道什么意思吗, 就是从 f[6][0]一路填到 f[6][25]

然后就是。。。f[5][…] … 然后就会填完了。 一些细节操作还没说!

数组的初始化

我们的数组的size,根据demo的情况,是一个 7 x 26的数组, 多出来的一行,是给那些匹配不上的字符用的!

最开始的情况,数组会被填成这个样子

我把值和对应的index都标出来了。

填写的过程

我们一行一行来!(发挥脑洞)

目前已知情况为 索引为6的一行被填好了

接下来我们来填写索引为5的一行

就是寻找 给定字符串“ahbgdc”中每个位置的字符 在 26个字母表中与之对应的位置

他是从a开始遍历到z的, 所以只有一个地方会匹配到,其余地方都匹配不到

匹配不到的地方都会按照 **下面一行同列 **来填入

所以 行列都为c的 就会变成当前的index 5 (为什么是5呢,这是有用的!它保存了该字符第一次出现的位置), 别的地方都照抄下面一行的

接下来我们来填写索引为4的一行

看吧,和上面说的一样, 每一行都是这么填的,然后就直接跳到搜索吧,重复地说没有啥意思哈~

填好了就是这个样子,这个表会保存每一个字母第一次出现的索引位置,便于寻找!

搜索过程

int add = 0;
for (int i = 0; i < n; i++) {
	if (f[add][s.charAt(i) - 'a'] == m) {
    	return false;
    }
    add = f[add][s.charAt(i) - 'a'] + 1;
}
return true;

这个是代码

我们搜索的是“abc”

add 代表的是 就是表格中行横着看的一行

大家结合代码部分推一推,我这部分实在不知道怎么说明了

  1. 我们可以发现字符a一上来就匹配到了,然后add就会指向匹配到的字符的下一行

    也就是add索引变为1

  2. 然后 第二个字符是b, s.charAt(i)会定位到b所在的列, 然后目前add的位置是 1 所以 定位的表格填入的是2,(如果不是6,就说明 能够匹配到值)然后就算是匹配上了,add就直接跳到匹配到字符的下一行(b的下一行,保存索引就是为了实现这个的操作。)

  3. 然后发现c也匹配上了,那么循环就结束了,返回true

可以设想一下,如果匹配的是abz会是什么情况呢?

ab能匹配到就不必多说,此时add就是第三个红箭头, 我们可以看到这一行 和 z列交叉的那个格子 存入的数是6, 那么就说明 ab的后面没有匹配过字符z

如果匹配abb呢?

add此时还是第三个箭头,但是b在第一次匹配之后, ab的后面就不存在第二个b了。

我添加一个带第二个b的图片,应该就能看明白了

第一次写这种类型的,妈耶,好难。。。希望能帮到别人。。。


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM