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