1. 程式人生 > >leetcode(16):最長公共子串/最長公共子序列

leetcode(16):最長公共子串/最長公共子序列

最近BAT等一線大廠面試的熱門題目,就是求最長公共子串子序列問題,Leetcode上面沒有完全相同的題目,但是有類似的題目,讓我們一起來看看。

1.leetcode#718. Maximum Length of Repeated Subarray

1.1題目描述

Given two integer arrays A and B, return the maximum length of an subarray that appears in both arrays.

Example :

Input:
A: [1,2,3,2,1]
B: [3,2,1,4,7]
Output: 3
Explanation:
The repeated subarray with maximum length is [3, 2, 1].

1.2思路

這其實是一道動態規劃的題目,其實意思就是找到當前節點之前連續的公共子串長度問題。一旦想到這裡,就知道其實狀態轉移就2種,要麼上一個匹配成功,要麼這一個匹配成功,其他情況都為0。如果我們用匹配矩陣來做,會非常好做。其思想就是,讓行為第一個字串,列為第二個字串,那麼Maching Metrix[row][col]則是第一個字串第row位置和第二個字串第col位置最大的匹配長度。只需要動筆畫一下,即可完全理解此方式。

1.3程式碼

public int findLength(int[] A, int[] B) {
        int max=0;
        int
[][] samecount=new int[A.length][B.length]; //初始化第一行第一列 for(int i=0;i<A.length;i++){ if(B[0]==A[i]){ samecount[i][0]=1; } } for(int i=0;i<B.length;i++){ if(B[i]==A[0]){ samecount[0][i]=1; } } //初始化最後一行
for(int row=1;row<A.length;row++){ for(int col=1;col<B.length;col++){ if(A[row]==B[col]){ //狀態轉移 samecount[row][col]=samecount[row-1][col-1]+1; //全域性比較 max=max>samecount[row][col]?max:samecount[row][col]; } } } return max; }

1.4更進一步

上面的方式非常容易理解,時間複雜度是O(m*n),空間複雜度也是O(m*n)。如果還想進一步壓縮,其實也是可以的,可以做到時間複雜度是O(min(m,n)),空間複雜度是O(max(m,n))。具體方法就是,我們把二位矩陣改成一維矩陣,然後通過迭代的方式來不斷的進行匹配,例如固定第二個字串,然後使用第一個字串的每一個字元都在第二個字串中匹配,把匹配結果迭代到下一次的匹配中即可。

1.5程式碼

public int findLength(int[] A, int[] B) {
        //時間複雜度小
        if(A.length<B.length){
            return findLength(B, A);
        }
        int max=0;
        int[] samecount=new int[A.length];
        //初始化第一行
        for(int i=0;i<A.length;i++){
            if(B[0]==A[i]){
                samecount[i]=1;
            }
        }
        //初始化最後一行
        for(int i=1;i<B.length;i++){
            int[] tempsamcount=new int[A.length];
            for(int j=0;j<A.length;j++){
                if(A[j]==B[i]){
                    if(j==0){
                        tempsamcount[j]=1;
                    }
                    else{
                        //狀態轉移
                        tempsamcount[j]=samecount[j-1]+1;
                    }
                    //全域性比較
                    max=max>tempsamcount[j]?max:tempsamcount[j];
                }
            }
            //下一次迭代
            samecount=tempsamcount;
        }
        return max;
    }

至於空間複雜度為O(1)的演算法,太難理解,因此我們這裡不進行講解。

2.leetcode#583. Delete Operation for Two Strings

2.1問題描述

Given two words word1 and word2, find the minimum number of steps required to make word1 and word2 the same, where in each step you can delete one character in either string.

Example :

Input: “sea”, “eat”
Output: 2
Explanation: You need one step to make “sea” to “ea” and another step to make “eat” to “ea”.

2.2思路

這道題原意思為,每次可以刪除一個字元,然後問刪除的最少次數,使得兩個字串相同。其實就是找到最長公共子序列即可。和最長公共子串一樣,我們只需要找到其公共子序列的狀態轉移即可,只不過這裡不需要連續,即不僅考慮它的上一狀態中相匹配的那一種情況Matching Metrix[row-1][col-1],還要考慮上一狀態中,剩餘的兩種情況,即Matching Metrix[row-1][col]和Matching Metrix[row][col-1]。

2.3程式碼

public static int minDistance(String word1, String word2) {
        if(word1==null||word1.length()==0){
            return word2.length();
        }
        else if(word2==null||word2.length()==0){
            return word1.length();
        }
        int max=0;
        //行為word1,列為word2;
        int[][] samecount=new int[word1.length()][word2.length()];
        //先把第一行處理掉
        for(int i=0;i<word1.length();i++){
            if(word1.charAt(i)==word2.charAt(0)){
                while(i<word1.length()){
                    samecount[i++][0]=1;
                }
                max=1;
            }
        }
        //再把第一列處理掉
        for(int i=0;i<word2.length();i++){
            if(word2.charAt(i)==word1.charAt(0)){
                while(i<word2.length()){
                    samecount[0][i++]=1;
                }
                max=1;
            }
        }
        //對後面的進行遍歷
        for(int row=1;row<word1.length();row++){
            for(int col=1;col<word2.length();col++){
                //狀態轉移
                samecount[row][col]=Math.max(samecount[row-1][col], samecount[row][col-1]);
                //如果當前相等的話,再看是否比目前最長的還長
                if(word1.charAt(row)==word2.charAt(col)){
                    samecount[row][col]=Math.max(samecount[row][col], samecount[row-1][col-1]+1);
                }
                //與全域性比較
                max=max>samecount[row][col]?max:samecount[row][col];
            }
        }
        return word1.length()+word2.length()-2*max;
    }

2.4更進一步

如同第一題一樣,我們仍然可以縮減空間複雜度,方法相同,不過處理起來會有一些麻煩。主要在於初始值的問題。

2.5程式碼

public static int minDistance(String word1, String word2) {
        if(word1==null||word1.length()==0){
            return word2.length();
        }
        else if(word2==null||word2.length()==0){
            return word1.length();
        }
        //假設word1長
        if(word1.length()<word2.length()){
            return minDistance(word2, word1);
        }
        int max=0;
        int[] samecount=new int[word1.length()];
        for(int i=0;i<samecount.length;i++){
            //只要有1個1,下面全部為1;
            if(word2.charAt(0)==word1.charAt(i)){
                while(i<samecount.length){
                    samecount[i++]=1;
                }
                max=1;
            }
        }
        for(int i=1;i<word2.length();i++){
            int[] tempsamecount=new int[word1.length()];
            for(int j=0;j<word1.length();j++){
                //如果是首位的話
                if(j==0){
                    //相等,則為1,否則為0
                    tempsamecount[j]=word1.charAt(j)==word2.charAt(i)?1:0;
                    //但是如果前面相等過了,則即使不相等也為1;
                    tempsamecount[j]=Math.max(tempsamecount[j], samecount[j]);
                }
                else{
                    //狀態轉移
                    //先看目前最長的子序列為多少
                    tempsamecount[j]=Math.max(tempsamecount[j-1], samecount[j]);
                    //如果相等當前相等的話,再看是否比目前最長的還長
                    if(word1.charAt(j)==word2.charAt(i)){
                        tempsamecount[j]=Math.max(tempsamecount[j], samecount[j-1]+1);
                    }
                }
                //與全域性比較
                max=max>tempsamecount[j]?max:tempsamecount[j];
            }
            //進行下一次遞迴
            samecount=tempsamecount;
        }
        return word1.length()+word2.length()-2*max;
    }

3.小結

這一小節中,我們主要學習了最長公共子串和最長公共子序列的問題,把它轉換為了一個匹配矩陣問題,自左上至右下進行搜尋,完全考慮到了序列化的時間先後順序。是一個非常通用的解法。