1. 程式人生 > >《程式設計師程式碼面試指南》求兩個字串最長公共子串

《程式設計師程式碼面試指南》求兩個字串最長公共子串

/**
 * 題目:
 * 給定兩個字串 str1 和 str2,返回兩個字串的最長公共子串。
 *舉例:
 * str1 = "1AB2345CD",str2 = "12345EF",返回"2345"。
 */

/**
 * 解答:
 * 經典動態規劃的方法可以做到時間複雜度為O(M*N),額外空間複雜度為O(M*N)。
 * 首先需要生成動態規劃表。生成大小為M*N的矩陣dp,行數為M,列數為N。dp[i][j]的含義是:
 * 在必須把str1[i]和str2[j]當作公共子串最後一個字元的情況下,最長公共子串能有多長。
 * 比如,str1 = "A1234B",str2 = "CD1234",dp[3][4]的含義是在必須把str1[3]和str2[4]當作公共子串
 * 最長能有多長。這時候最長公共子串為"123"所以dp[3][4]為3。
 * 再如,str1 = "A12E4B",str2 = "CD12F4",dp[3][4]的含義是必須把str1[3]和str2[4]當作公共子串的最後一個
 * 字元的情況下,公共子串能有多長。顯然這種情況不可能構成公共子串。dp[3][4]為0。
 */

/**
 * 1.矩陣dp第一列即dp[0..M-1][0]。對某一個位置(i,0)來說,如果str[i] == str2[0],令dp[i][0] = 1,
 * 否則令dp[i][0] = 0。
 * 比如str1 = "ABAC" str2 = "A"。dp矩陣第一列上的值依次為dp[0][0] = 1,dp[2][0] = 0,dp[3][0] = 1,
 * dp[3][0] = 0。
 * 2.矩陣dp第一行dp[0][0...N-1]與步驟1同理。對某一個位置(0,j)來說,如果str1[0] == str2[j],令
 * dp[0][j] = 1,否則令dp[0][j] = 0。
 * 3.其他位置按照從左到右,再從上到下來計算,dp[i][j]的值只能有兩種情況。
 * 3.1如果str1[i] != str2[j]說明在必須把str1[i]和str2[j]作為公共子串的最後一個字元是不可能的,令
 * dp[i][j] = 0。
 * 3.2如果str1[i] == str[j]說明str1[i]和str2[j]可以作為公共子串最後一個字元,從最後一個字元能向
 * 左擴多大的長度呢?就是dp[i-1][j-1]的值,所以令dp[i][j] = dp[i-1][j-1]+1。
 */

/**
 * 生成動態規劃表之後,得到最長公共子串是非常容易的。
 */
/**
 * 題目:
 * 給定兩個字串 str1 和 str2,返回兩個字串的最長公共子串。
 *舉例:
 * str1 = "1AB2345CD",str2 = "12345EF",返回"2345"。
 */

/**
 * 解答:
 * 經典動態規劃的方法可以做到時間複雜度為O(M*N),額外空間複雜度為O(M*N)。
 * 首先需要生成動態規劃表。生成大小為M*N的矩陣dp,行數為M,列數為N。dp[i][j]的含義是:
 * 在必須把str1[i]和str2[j]當作公共子串最後一個字元的情況下,最長公共子串能有多長。
 * 比如,str1 = "A1234B",str2 = "CD1234",dp[3][4]的含義是在必須把str1[3]和str2[4]當作公共子串
 * 最長能有多長。這時候最長公共子串為"123"所以dp[3][4]為3。
 * 再如,str1 = "A12E4B",str2 = "CD12F4",dp[3][4]的含義是必須把str1[3]和str2[4]當作公共子串的最後一個
 * 字元的情況下,公共子串能有多長。顯然這種情況不可能構成公共子串。dp[3][4]為0。
 */

/**
 * 1.矩陣dp第一列即dp[0..M-1][0]。對某一個位置(i,0)來說,如果str[i] == str2[0],令dp[i][0] = 1,
 * 否則令dp[i][0] = 0。
 * 比如str1 = "ABAC" str2 = "A"。dp矩陣第一列上的值依次為dp[0][0] = 1,dp[2][0] = 0,dp[3][0] = 1,
 * dp[3][0] = 0。
 * 2.矩陣dp第一行dp[0][0...N-1]與步驟1同理。對某一個位置(0,j)來說,如果str1[0] == str2[j],令
 * dp[0][j] = 1,否則令dp[0][j] = 0。
 * 3.其他位置按照從左到右,再從上到下來計算,dp[i][j]的值只能有兩種情況。
 * 3.1如果str1[i] != str2[j]說明在必須把str1[i]和str2[j]作為公共子串的最後一個字元是不可能的,令
 * dp[i][j] = 0。
 * 3.2如果str1[i] == str[j]說明str1[i]和str2[j]可以作為公共子串最後一個字元,從最後一個字元能向
 * 左擴多大的長度呢?就是dp[i-1][j-1]的值,所以令dp[i][j] = dp[i-1][j-1]+1。
 */

/**
 * 生成動態規劃表之後,得到最長公共子串是非常容易的。
 */
public class LongestPublicCharacterString {
    public static int[][] getdp(char[] str1, char[] str2){
        int[][] dp = new int[str1.length][str2.length];
        for(int i = 0; i < str1.length; i++){
            if(str1[i] == str2[0]){
                dp[i][0] = 1;
            }
        }
        for(int j = 0; j < str2.length; j++){
            if(str2[j] == str1[0]){
                dp[0][j] = 1;
            }
        }
        for(int i = 1; i < str1.length; i++){
            for(int j = 1; j < str2.length; j++){
                if(str1[i] == str2[j]){
                    dp[i][j] = dp[i-1][j-1] + 1;
                }
            }
        }
        return dp;
    }

    public  static String lcst1(String str1, String str2){
        if(str1 == null || str2 == null || str1.equals("") || str2.equals("")){
            return "";
        }
        char[] chs1 = str1.toCharArray();
        char[] chs2 = str2.toCharArray();
        int[][] dp = getdp(chs1,chs2);
        int end = 0;
        int max = 0;
        for(int i = 0; i < chs1.length; i++){
            for(int j = 0; j < chs2.length; j++){
                if(dp[i][j] > max){
                    end = i;
                    max = dp[i][j];
                }
            }
        }
        return str1.substring(end - max + 1, end  + 1);
    }

    public static void main(String[] args) {
        String str1 = "abcbcbbc";
        String str2 = "cbcbaaa";
        System.out.println(lcst1(str1, str2));
    }
}

參考資料:《程式設計師面試程式碼指南》左程雲 著