最長公共子串等最字問題
阿新 • • 發佈:2019-02-18
一. 前言
最近做筆試題,碰到了很多”最”型別的題,像 最長公共子串|最長公共子序列|最長遞增子序列|最長連續子陣列的最大和| 新增(刪除)元素,使其成為和最小的迴文序列|新增最少元素,使其成為迴文串. 鑑於他們之間有些存在一些共性,故在這裡做個總結.
二. “最” 字題型
- 最長公共連續子串
- 最長公共子序列
- 最長遞增子序列
- 最長連續子陣列的最大和
- 新增(刪除)元素,使其成為和最小的迴文序列
- 新增最少元素,使其成為迴文串.
下面分別給出他們的題解.
三. “最”字問題題解
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;i<a.length();i++){
for(int j=0;j<b.length();j++){
if (a.charAt(i) == b.charAt(j)) {
matrix[i][j] = 1;
}
}
}
int len;
int maxLen = 0;
for(int i=0;i<a.length();i++){
for(int j=0;j<b.length();j++){
len = 0;
int m = i;
int n = j;
while(matrix[m++][n++] == 1){ //上面陣列調大一點,防止越界
len++;
}
if (maxLen<len) {
maxLen = len;
}
}
}
//如果需要列印最小子串,記錄最後子串的位置.
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;i<n;i++){
a[i] = cin.nextInt();
}
int[] dp = new int[n];
int lis = 0;
for(int i=0;i<n;i++){
dp[i] = 1;
for(int j=0;j<i;j++){
if (a[i]>a[j] && dp[i]<dp[j]+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;i<n;i++){
arr[i] = cin.nextInt();
if (currSum<=0) {
currSum = arr[i];
}else {
currSum +=arr[i];
}
if (maxSum <currSum) {
maxSum = currSum;
}
}
System.out.println(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<s.length;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.
完)