迴文字符串

最近遇到兩個題目,比較有意思,因爲兩個題目的描述比較類似,在這裏就一塊兒說了,作一個比較java

題目一:給定一個字符串,給該字符串添加一些字符,使其成爲一個迴文串,求須要添加的最少字符數,並求出添加字符後迴文串的樣子,若是有多個這樣的迴文串,只用返回其中一個便可數組

好比: str="AB"  那麼,只用在 "A" 以前添加一個B,就能夠造成迴文  「ABA」spa

            str="A"    那麼,不用添加,就已是迴文了code

            str="ACDC" , 那麼在最後添加一個A就能夠造成迴文 "ACDCA"blog

思路:此題能夠用動態規劃進行操做,開一個數組dp[i][j], 表示要想使得 i  到  j 這段長度的字符串成爲迴文字符串,至少須要添加多少個字符,咱們都知道動態規劃須要分析通項公式,可是有一些不須要依賴其餘字符串

元素,能夠獨立推出來的值,咱們須要單獨處理:當i=j, 即字符串的長度爲1時,這個字符串確定是迴文的,當字符串長度大於2時,有以下狀況class

(1)若是 i 和 j 相鄰 , 而且str[i]==str[j], 那麼dp[i][j]=0, 若是 str[i] != str[j]  ,那麼dp[i][j]=1;表示須要添加一個字符串import

(2)若是 i 和 j 不相鄰,  但str[i]==str[j], 那麼dp[i][j]=dp[i+1][j-1],表示只要 i 和 j 內部的字符串組成迴文後,i 到 j 這部分就天然成爲迴文了 ,若是 str[i] != str[j]  ,那麼dp[i][j]=min(dp[i][j-1],dp[i+1][j])+1, 表示只要單邊造成迴文串後,另外一邊添加對邊的值便可成爲迴文,此時有兩種補法,因此須要取最小的List

注意到上面動態規劃時,dp[i][j] = dp[i+1][j-1]等表達式,隱含的條件是,外面的值須要依賴裏面的值,因此咱們在填表時,須要注意由裏面向外面擴散im

求出dp表之後,再按照兩邊向中間的順序進行還原,就能夠還原出迴文字符串了,好比,當str[left] 和 str[right] 相等時,直接複製到copy[ ] 數組中去,若是str[left] 和 str[right] 不相等,那麼就比較dp[left][right-1] 和 dp[left][right-1] 的值,看看補哪邊須要的字符串最少,具體代碼以下

 1 import java.util.ArrayList;
 2 import java.util.Deque;
 3 import java.util.LinkedList;
 4 import java.util.Scanner;
 5 import java.util.Stack;
 6 /*給定字符串,添加最少成爲迴文字符串,採用動態規劃*/
 7 public class Main {
 8     public static void main(String[] args) {
 9         Scanner scan = new Scanner(System.in);
10         String str = scan.nextLine();
11         char s[] = str.toCharArray();
12         int dp[][] = new int[s.length][s.length];   //dp[i][j] 表示 i 到 j 成爲迴文字符須要添加的最少字符數量
13         for (int j=1;j<s.length;j++) {     //結尾座標,依次遞增
14             for (int i=j-1;i>=0;i--) {    //由內向外擴散
15                 if (s[i]==s[j]) {
16                     if (i==j-1)    dp[i][j]=0;
17                     else dp[i][j] = dp[i+1][j-1];
18                 }else {
19                     dp[i][j] = Math.min(dp[i][j-1], dp[i+1][j])+1;
20                 }
21             }
22         }
23         char res[] = new char[s.length+dp[0][s.length-1]];  //最終須要的長度
24         int i=0;int left=0;
25         int j=s.length-1; 
26         int right=res.length-1;
27         while(i<=j) {
28             if (s[i]==s[j]) {
29                 res[left++]=s[i++];
30                 res[right--]=s[j--];
31             }else {
32                 if (dp[i][j-1]<dp[i+1][j]) {
33                     res[left++]=s[j];
34                     res[right--]=s[j--];
35                 }else {
36                     res[left++]=s[i];
37                     res[right--] = s[i++];
38                 }
39             }
40         }
41         for (int k=0;k<res.length;k++) {
42             System.out.print(res[k]);
43         }
44     }
45 }

 題目二:給定一個字符串,給該字符串刪除一些字符,使其成爲一個迴文串,求須要刪除的最少字符數,並求出刪除字符後迴文串的樣子,若是有多個這樣的迴文串,只用返回其中一個便可

好比  str="A"   那麼不用刪除任何子串,就可造成迴文「A」

         str="AB" 那麼能夠刪除「A」 ,就能夠造成迴文「B」

         str="ABAD"  那麼能夠刪除「D」 就能夠造成迴文「ABA」

第二道題目雖然題目和第一題很像,但解法卻不同,能夠採用倒轉原字符串,造成新的字符串str2,而後再求str1與str2的最長公共子序列便可,求最長公共子序列須要開一個二維數組dp[i][j],表示str1的前 i個字符和str2的前 j個字符的最長

公共子序列,若是i 和 j 相等, 那麼 dp[i][j] = dp[i-1][j-1]+1, 若是 i 和 j 不相等,那麼取dp[i][j-1] 和 dp[i-1][j] 兩個字串的長度中選一個最大的,具體代碼以下:

 1 import java.util.*;
 2 /*給定字符串,刪除最少成爲迴文字符串,採用動態規劃*/
 3 public class Main {
 4     public static void main(String[] args) {
 5         Scanner scan = new Scanner(System.in);
 6         String str = scan.nextLine();
 7         char s1[] = str.toCharArray();
 8         char s2[] = reverse(str.toCharArray());
 9         int dp[][] = new int [str.length()][str.length()];  // 記錄最長子序列
10         boolean flag=false;
11         //對第0行單獨處理
12         for (int j=0;j<s2.length;j++) {
13             if (flag || s1[0]==s2[j]) {
14                 dp[0][j]=1;
15                 flag=true;
16             }
17         }
18         flag=false;
19         //對第0列單獨處理
20         for (int j=0;j<s1.length;j++) {
21             if (flag || s1[j]==s2[0]) {
22                 dp[j][0]=1;
23                 flag=true;
24             }
25         }
26         //求DP矩陣
27         for (int i=1;i<s1.length;i++) {
28             for(int j=1;j<s2.length;j++) {
29                 if (s1[i]==s2[j]) {
30                     dp[i][j] = dp[i-1][j-1]+1;
31                 }else if (dp[i-1][j]>dp[i][j-1]) {
32                     dp[i][j] = dp[i-1][j];
33                 }else {
34                     dp[i][j]=dp[i][j-1];
35                 }
36             }
37         }
38         
39         //反推最長子序列
40         int len = dp[s1.length-1][s2.length-1];
41         int i = s1.length-1;
42         int j=s2.length-1;
43         while(len>0) {
44             if (i>=1&&dp[i][j]==dp[i-1][j]) {
45                 i--;
46             }else if (j>=1 && dp[i][j]==dp[i][j-1]) {
47                 j--;
48             }else {
49                 System.out.print(s1[i]);   //與前面的不等,說明這是最長子序列裏面的元素,輸出
50                 len--;
51                 i--;
52                 j--;
53             }
54         }
55     }
56     public static char[] reverse(char c[]) {
57         int i=0;  
58         int j=c.length-1;
59         while(i<j) {
60             char temp = c[i];
61             c[i++]=c[j];
62             c[j--]=temp;
63         }
64         return c;
65     }
66 }
相關文章
相關標籤/搜索