首页 > 代码库 > 字符串整理

字符串整理

 

 

 

最长公共子序列(longest common subsequence)

二维dp

  状态dp[i][j]表示字符串x的前缀xi和字符串y的前缀yj能够构成的最长公共子序列的长度。

  初始化:第0行和第0列的dp[i][0] 和 dp[0][j]都设为0.

  递推:dp[i][j]=dp[i-1][j-1]+1  if(x[i]==y[j]) ; dp[i][j]= max(dp[i-1][j],dp[i][j-1])  if(x[i]!=y[j])

打印路径:1. 可以用符号记录每次选择的方向,然后从dp[m][n]向前根据符号递归打印路径。 

     2. 可以不用记录选择方向,每次根据dp[i][j] 与 d[i-1][j-1],dp[i-1][j],dp[i][j-1]的关系找出路径。

      两者打印复杂度都是O(m+n)。

空间优化:因为计算dp[i][j]只依赖于前面的一行和一列,所以可以用dp[2][n]的空间就够了,循环使用。

 

public class StringConclude {    public static int LCS(String x, String y) {        int m = x.length();        int n = y.length();        int[][] dp = new int[m + 1][n + 1];        char[][] path = new char[m + 1][n + 1];        for (int i = 1; i <= m; i++) {            for (int j = 1; j <= n; j++) {                if (x.charAt(i - 1) == y.charAt(j - 1)) {                    dp[i][j] = dp[i - 1][j - 1] + 1;                    path[i][j] = \\;                } else if (dp[i - 1][j] >= dp[i][j - 1]) {                    path[i][j] = |;                    dp[i][j] = dp[i - 1][j];                } else {                    path[i][j] = -;                    dp[i][j] = dp[i][j - 1];                }            }        }        printLCS(path, x, m, n);        System.out.println();        printLCS(dp, x, m, n);        System.out.println();        return dp[m][n];    }    private static void printLCS(int[][] dp, String x, int i, int j) {        if (i == 0 || j == 0)            return;        int maxIdx = myMaxIdx(dp[i - 1][j - 1], dp[i - 1][j], dp[i][j - 1]);        if (maxIdx == 0) {            printLCS(dp, x, i - 1, j - 1);            System.out.print(x.charAt(i - 1));        } else if (maxIdx == 1) {            printLCS(dp, x, i - 1, j);        } else {            printLCS(dp, x, i, j - 1);        }    }    private static int myMaxIdx(int a, int b, int c) {        int max = a;        int maxIdx = 0;        if (b > max) {            max = b;            maxIdx = 1;        }        if (c > max) {            max = c;            maxIdx = 2;        }        return maxIdx;    }    private static void printLCS(char[][] path, String x, int i, int j) {        if (i == 0 || j == 0)            return;        if (path[i][j] == \\) {            printLCS(path, x, i - 1, j - 1);            System.out.print(x.charAt(i - 1));        } else if (path[i][j] == |)            printLCS(path, x, i - 1, j);        else            printLCS(path, x, i, j - 1);    }    public static void main(String[] args) {        System.out.println(LCS("abcbdab", "bdcaba"));        System.out.println(LCS("aaaa", "aaaa"));        System.out.println(LCS("abab", "baba"));    }}

 

 

最长公共子串(longest common substring

二维dp

  状态dp[i][j]表示字符串x以x[i]结尾的子串和字符串y以y[j]结尾的子串能构成的最长公共子串的长度。

  初始化:第0行和第0列的dp[i][0] 和 dp[0][j]都设为0.

  递推:dp[i][j]=dp[i-1][j-1]+1  if(x[i]==y[j]) ; dp[i][j]= 0  if(x[i]!=y[j])

打印路径:只需记录最长的情况下结尾的坐标即可,因为是连续的,所以可以根据最长长度向前找到子串。

空间优化:因为计算dp[i][j]只依赖于前面的一行和一列,所以可以用dp[2][n]的空间就够了,循环使用。

    public static int LCSubstring(String x, String y) {        int m = x.length();        int n = y.length();        int[][] dp = new int[m + 1][n + 1];        int res = 0;        int xTailIdx = -1;        for (int i = 1; i <= m; i++) {            for (int j = 1; j <= n; j++) {                if (x.charAt(i - 1) == y.charAt(j - 1)) {                    dp[i][j] = dp[i - 1][j - 1] + 1;                    if (dp[i][j] > res) {                        res = dp[i][j];                        xTailIdx = i;                    }                } else {                    dp[i][j] = 0;                }            }        }        // print the result        System.out.println(x.substring(xTailIdx - res, xTailIdx));        return res;    }

 

字符串整理