• 字符串整理


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

    最长公共子串(longest common substring

    最长递增子序列(longest increasing subsequence)

    最长重复子串

    最长不重复子串

    最长回文子串

    两个字符串的编辑距离(edit distance)

    最长公共子序列(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;
    
        }

    最长递增子序列(longest increasing subsequence)

    定义d[i] 表示能构成长度为(i+1)的LIS的序列最后元素的最小值,根据定义d[i+1]>d[i],所以d[i]是有序的,所以对于新的元素,可以用二分查找更新特定元素的位置。

    public class StringConclude {
        
        /**
         * 最长递增子序列 LIS 
         * DP + BinarySearch
         *  
         */
        public static int LIS(int[] a) {
            if (a == null || a.length == 0)
                return 0;
            int len = 1;/* 存储子序列的最大长度 即MaxV当前的下标 */
            int[] dp = new int[a.length];/* 存储长度i+1(len)的子序列最大元素的最小值 */
            dp[0] = a[0];
            for (int i = 1; i < a.length; i++) {
                if (a[i] > dp[len - 1]) {
                    dp[len++] = a[i];
                } else {
                    int pos = bSearch(dp, len, a[i]);
                    dp[pos] = a[i];
                }
            }
    
            return len;
        }
    
        /* 返回MaxV[i]中刚刚大于x的那个元素的下标 */
        private static int bSearch(int[] maxV, int len, int target) {
            int left = 0, right = len - 1;
            while (left <= right) {
                int mid = left + (right - left) / 2;
                if (maxV[mid] <= target) {
                    left = mid + 1;
                } else {
                    right = mid - 1;
                }
            }
            return left;
        }
        
    
        public static void main(String[] args) {
            System.out.println(LIS(new int[]{1, -3, 2, -1, 4, -5, 6, -7 }));
    
        }
    }

    最长重复子串:

    根据字符串生成后缀数组,然后对后缀数组排序,比较相邻的后缀寻找最长的重复子串。

    import java.util.Arrays;
    
    public class Solution {
    
        /**
         * 最长重复子串
         */
        public static int LRS(String s) {
            if (s == null || s.isEmpty())
                return 0;
            int n = s.length();
            String[] suffix = new String[n];
            for (int i = 0; i < n; i++) {
                suffix[i] = s.substring(i);
            }
    
            Arrays.sort(suffix);
    
            int maxLen = 0;
            int startIdx = -1;
    
            for (int i = 0; i < n - 1; i++) {
                int tmpLen = commonLen(suffix[i], suffix[i + 1]);
                if (tmpLen > maxLen) {
                    maxLen = tmpLen;
                    startIdx = i;
                }
            }
            System.out.println(s.substring(startIdx, startIdx + maxLen));
            return maxLen;
        }
    
        private static int commonLen(String a, String b) {
            if (a == null || b == null)
                return 0;
            int i = 0, j = 0;
            int res = 0;
            while (i < a.length() && j < b.length()) {
                if (a.charAt(i) == b.charAt(j)) {
                    res++;
                } else
                    break;
                i++;
                j++;
            }
            return res;
        }
    
        public static void main(String[] args) {
            System.out.println(LRS("banana"));
        }
    
    }

    最长不重复子串: 

    双指针+hash法。

    O(n)时间,O(1)空间。 

    public class Solution {
    
        /**
         * 最长不重复子串
         */
        public static int LNRS(String s) {
            if (s == null || s.isEmpty())
                return 0;
            boolean[] exist = new boolean[256];
            int i = 0, j = 0;
            int maxLen = 0;
            int startIdx = -1;
            for (; j < s.length(); j++) {
                if (!exist[s.charAt(j)]) {
                    exist[s.charAt(j)] = true;
                    continue;
                }
    
                // exist
                if (j - i > maxLen) {
                    maxLen = j - i;
                    startIdx = i;
                }
    
                while (i < j) {
                    if (s.charAt(i) != s.charAt(j))
                        exist[i] = false;
                    i++;
    
                    if (s.charAt(i - 1) == s.charAt(j))
                        break;
                }
    
            }
            if (j - i > maxLen) {
                maxLen = j - i;
                startIdx = i;
            }
    
            System.out.println(s.substring(startIdx, startIdx + maxLen));
            return maxLen;
        }
    
        public static void main(String[] args) {
            System.out.println(LNRS("abcabdefac"));
        }
    
    }

    最长回文子串:

    http://www.cnblogs.com/jdflyfly/p/3810674.html

    两个字符串的编辑距离

    http://www.cnblogs.com/jdflyfly/p/3812776.html

  • 相关阅读:
    机器学习框架之sklearn简介
    ubuntu 16.04下使用 python pip的安装问题。
    ubuntu 16.04 搭建git小型服务器
    使用config 来管理ssh的会话
    【linux】su、sudo、sudo su、sudo -i的用法和区别
    【python】确保文件写入结束
    【python】描述符descriptor
    【python】类(资料+疑惑)
    【pymongo】mongodb cursor id not valid error
    【python】继承关系和isinstance
  • 原文地址:https://www.cnblogs.com/jdflyfly/p/3959179.html
Copyright © 2020-2023  润新知