最长公共子串等最字问题

一. 前言

最近做笔试题,碰到了很多”最”类型的题,像 最长公共子串|最长公共子序列|最长递增子序列|最长连续子数组的最大和| 添加(删除)元素,使其成为和最小的回文序列|添加最少元素,使其成为回文串. 鉴于他们之间有些存在一些共性,故在这里做个总结.

二. “最” 字题型

  1. 最长公共连续子串
  2. 最长公共子序列
  3. 最长递增子序列
  4. 最长连续子数组的最大和
  5. 添加(删除)元素,使其成为和最小的回文序列
  6. 添加最少元素,使其成为回文串.

下面分别给出他们的题解.

三. “最”字问题题解

1. 最长连续公共子串

思路:
解法就是用一个矩阵来记录两个字符串中所有位置的两个字符之间的匹配情况,若是匹配则为1,否则为0。然后求出对角线最长的1序列,其对应的位置就是最长匹配子串的位置.

package com.dingding.LCS_LIS;
/**
 * 最长公共连续子串(连续) 
 * 思路: 最长的对角线元素1
 *  09-22 
 */
import java.util.Scanner;

public class Main5 {

    public static void main(String[] args) {
        Scanner cin = new Scanner(System.in);
        while(cin.hasNext()){
            String a = cin.next();
            String b = cin.next();

            int[][] matrix = new int[50][50];
            for(int i=0;ifor(int j=0;jif (a.charAt(i) == b.charAt(j)) {
                        matrix[i][j] = 1;
                    }
                }
            }

            int len;
            int maxLen = 0;
            for(int i=0;ifor(int j=0;j0;
                    int m = i;
                    int n = j;
                    while(matrix[m++][n++] == 1){  //上面数组调大一点,防止越界
                        len++;
                    }

                    if (maxLen//如果需要打印最小子串,记录最后子串的位置.
            System.out.println(maxLen);
        }
    }
}

2. 最长公共子序列


package com.dingding.LCS_LIS;
/**
 * 最长公共子序列(不连续) - LCS问题
 * 动态规划问题: 最优子结构/边界/转移方程
 * 思路: 找出最优子结构,转移方程.
 * dp[i][j] 表示数组a[0..i]和b[0..j]的最长公共子串的长度.
 * dp[i][j] = dp[i-1][j-1]+1  当 a[i]=b[j]
 * dp[i][j] = max{dp[i][j-1],dp[i-1][j]} 当 a[i]!=b[j]
 *  09-21
 */
import java.util.Scanner;

public class Main4 {

    public static void main(String[] args) {
        Scanner cin = new Scanner(System.in);
        while(cin.hasNext()){
            String a = cin.next();
            String b = cin.next();

            int[][] dp = new int[a.length()+1][b.length()+1];
            for(int i=1;i<=a.length();i++){
                for(int j=1;j<=b.length();j++){
                    if (a.charAt(i-1)==b.charAt(j-1)) {
                        dp[i][j] = dp[i-1][j-1]+1;
                    }else {
                        dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]);
                    }
                }
            }
            System.out.println(dp[a.length()][b.length()]);
        }
    }
}

3. 最长递增子序列

package com.dingding.LCS_LIS;
/**
 * 最长递增子序列(LIS)
 * 思路: dp
 * dp[i] 表示前i个字符中最长递增子序列的长度.
 *  09-22 
 */
import java.util.Scanner;

public class Main7 {

    public static void main(String[] args) {
        Scanner cin = new Scanner(System.in);
        while(cin.hasNext()){
            int n = cin.nextInt();
            int[] a = new int[n];
            for(int i=0;iint[] dp = new int[n];
            int lis = 0;
            for(int i=0;i1;
                for(int j=0;jif (a[i]>a[j] && dp[i]1) {
                        dp[i] = dp[j] + 1;
                        if (dp[i]>lis) {
                            lis = dp[i];
                        }
                    }
                }
            }
            System.out.println(lis);
        }
    }
}

4. 最长连续子数组的最大和

package com.dingding.LCS_LIS;
/**
 * 最长连续子序列的最大和
 * 思路: 用一变量curSum记录当前的最大和,若<=0,则为a[i],否则为curSum += a[i];
 *  09-22 
 */
import java.util.Scanner;

public class Main6 {

    public static void main(String[] args) {
        Scanner cin = new Scanner(System.in);
        while(cin.hasNext()){
            int n = cin.nextInt();
            int[] arr = new int[n];

            int currSum = 0;
            int maxSum = 0;
            for(int i=0;iif (currSum<=0) {
                    currSum = arr[i];
                }else {
                    currSum +=arr[i];
                }
                if (maxSum 

5. 添加(删除)元素,使其成为和最小的回文序列

package com.dingding.LCS_LIS;
/**
 * 添加或删除元素,使其成为最小的回文序列.
 * 动态规划问题: 最优子结构/边界/转移方程
 * 思路: 将数组逆序得到数组b,求数组的最大公共子序列的和dp[i][j],然后2*sum-dp[n][n],即得到最小回文序列的和.
 * dp[i][j] 表示数组a[0..i]和b[0..j]的最长公共子序列的和.
 * dp[i][j] = dp[i+1][j-1]+a[i]  当 a[i]=b[j]
 * dp[i][j] = max{dp[i][j-1],dp[i-1][j]}
 *  09-21
 */
import java.util.Scanner;

public class Main3 {

    public static void main(String[] args) {
        Scanner cin = new Scanner(System.in);
        while(cin.hasNext()){
            int n = cin.nextInt();
            int[] a = new int[n+1];
            int[] b = new int[n+1];
            for(int i=1;i<=n;i++){
                a[i] = cin.nextInt();
                b[n-i+1] = a[i];
            }

            int[][] dp = new int[n+1][n+1];
            int sum = 0;
            for(int i=1;i<=n;i++){
                sum += a[i];
                for(int j=1;j<=n;j++){
                    if (i>=1 && a[i]==b[j]) {
                        dp[i][j] = dp[i-1][j-1]+a[i];
                    }else {
                        dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]);
                    }
                }
            }
            int result = 2*sum - dp[n][n];
            System.out.println(result);
        }
    }
}

6. 添加最少元素,使其成为回文串.

package com.dingding.LCS_LIS;
/**
 * 删除最少的字符,使其成为回文字符串.  和增加问题相同解法.
 * 动态规划问题: 最优子结构/边界/转移方程
 * dp[i][j] 表示s[i]~s[j],使其成为回文的最少插入字符数.
 * dp[i][j] = dp[i+1][j-1]  当 s[i]=s[j]
 * dp[i][j] = min{dp[i+1][j],dp[i][j-1]}+1; 当 s[i]!=s[j]
 *  09-21
 */
import java.util.Scanner;

public class Main2 {

    public static void main(String[] args) {
        Scanner cin = new Scanner(System.in);
        while(cin.hasNext()){
            String str = cin.nextLine();
            char[] s = str.toCharArray();

            int[][] dp = new int[s.length][s.length];
            for(int i=s.length-1;i>=0;i--){
                for(int j=i+1;j
                    if (s[i]==s[j]) {
                        dp[i][j] = dp[i+1][j-1];
                    }else {
                        dp[i][j] = Math.min(dp[i+1][j], dp[i][j-1])+1;
                    }
                }
            }
            System.out.println(dp[0][s.length-1]);
        }
    }
}

这题有一些优化的解法,见参考文献1.


完)

参考文献

  1. 最长公共子序列|最长公共子串|最长重复子串|最长不重复子串|最长回文子串|最长递增子序列|最大子数组和
  2. 最长公共子串(LCS)

你可能感兴趣的:(编程之美,剑指offer-java)