Problem:
Description
1. Empty sequence is a regular sequence.
2. If S is a regular sequence, then (S) and [S] are both regular sequences.
3. If A and B are regular sequences, then AB is a regular sequence.
For example, all of the following sequences of characters are regular brackets sequences:
(), [], (()), ([]), ()[], ()[()]
And all of the following character sequences are not:
(, [, ), )(, ([)], ([(]
Some sequence of characters '(', ')', '[', and ']' is given. You are to find the shortest possible regular brackets sequence, that contains the given character sequence as a subsequence. Here, a string a1 a2 ... an is called a subsequence of the string b1 b2 ... bm, if there exist such indices 1 = i1 < i2 < ... < in = m, that aj = bij for all 1 = j = n.
Input
Output
Sample Input
([(]
Sample Output
()[()]
思路:動態規劃問題
從這個問題重新總結實現動態規劃問題的思路,首先確定子結構,因爲動態規劃問題的一大特性,就是子結構擁有父結構的全部性質,只是將問題規模變小了而已,那麼我們就先找一下這個問題的子結構。當我們看到一個( 時首先看能不能旁邊的匹配,然後看能不能和字符串中對應的位置進行匹配,也就是第一個和最後一個,顯然括號匹配問題上來說,首尾匹配擁有更好的子結構的性質,因爲如果首尾匹配成功的話,相當於轉換成子問題,除去首尾後元素的匹配結果。這很好的具有子結構的特點。那麼如果首尾不匹配的情況呢。那就說明,我首尾匹配的符號如果存在的話,一定在首尾中間,所以我們只需要遍歷首尾中間的元素,然後以中間進行分隔,轉換成求首到中間元素,和中間原素到結尾的需要添加符號數之和的最小值,來確定最優的中間解就好,這樣也就很好的轉換成了問題相同的子問題。然後我們定義一個二維數組time[i][j]用來記錄元素從i到j所需添加括號的個數,我們依次寫出狀態轉移方程:
if time[i]==time[j] 則 time[i][j]=time[i+1][j-1] 這就是剔除首尾元素的子問題。
if time[i]!=time[j] 則我們可以需要從中找出中間元素k, time[i][j]=min{time[i][j],time[i][k]+time[k+1][j]}使得time[i][j]最小。
最後我們就可以進行編程實現了。
public class bracketsSequence {
public static void main(String[] args) {
Scanner in =new Scanner(System.in);
String line=in.nextLine();
//子結構i和j if ai==aj a[i][j]=a[i+1][j-1]
//if ai!=aj ai到中間某個值,和中間某個值到aj的添加次數之和,然後找到使數字之和最小的中間值
//因此,採取一個二維數組來表示從i到j所需要添加的最小次數。
int length=line.length();
int [][]time=new int[length][length];//我們要注意我們定義的這個二維數組下半矩陣是沒有數據的,因爲我們是從i到j,i<=j
for(int i=0;i<length;i++) {
//注意這裏一定要先把i i賦值爲1,因爲這是後面算法遍歷不到的情況,這就是當只一個字母時,需要插入一個括號。
time[i][i]=1;
}
for(int step=1;step<length;step++) {//定義步數,先從小步完成,這樣執行大步的時候,可以使用小步結果。
for(int i=0;i<length-step;i++) {
int j=i+step;
time[i][j]=99;//初始化爲一個較大的值,方便找到最小的值
if(match(line.charAt(i),line.charAt(j))) {
time[i][j]=time[i+1][j-1];
}else {
for(int k=i;k<j;k++) {//找到中間能使插入最小的k
time[i][j]=Math.min(time[i][j],time[i][k]+time[k+1][j]);
}
}
}
}
for(int i=0;i<length;i++){//這裏我輸出了一下time數組方便理解
System.out.println(Arrays.toString(time[i]));
}
System.out.println(time[0][length-1]);
print(0,length-1,line,time);
}
public static boolean match(char a,char b) {
if(a=='('&&b==')')
return true;
if(a=='['&&b==']')
return true;
return false;
}
public static void print (int i,int j,String line,int[][]d) {//採用遞歸的方法,輸出
//經典格式,先判斷參數合法性,然後處理特殊情況
if(i>j) return;
if(i==j){
if(line.charAt(i)=='('||line.charAt(i)==')') System.out.print("()");
if(line.charAt(i)=='['||line.charAt(i)==']') System.out.print("[]");
return;
}
int ans=d[i][j];
//若前後兩端匹配
if(ans==d[i+1][j-1]&&match(line.charAt(i),line.charAt(j))){
System.out.print(line.charAt(i));
print(i+1,j-1,line ,d);
System.out.print(line.charAt(j));
return;
}
//前後兩端不匹配
for(int k=i;k<j;k++){
if(ans==d[i][k]+d[k+1][j]){
print(i,k,line,d);
print(k+1,j,line,d);
return;
}
}
}
}
結果:
([(] //輸入
[1, 2, 3, 2]
[0, 1, 2, 1]
[0, 0, 1, 2]
[0, 0, 0, 1]
2
()[()]
存在問題:當我輸入下面的情況時,結果明顯不對。
所以這個問題也許不應該用動態規劃來做,也許用棧來做纔是對的
([)([])//輸入
[1, 2, 1, 2, 3, 2, 3]
[0, 1, 2, 3, 4, 3, 2]
[0, 0, 1, 2, 3, 2, 1]
[0, 0, 0, 1, 2, 1, 0]
[0, 0, 0, 0, 1, 0, 1]
[0, 0, 0, 0, 0, 1, 2]
[0, 0, 0, 0, 0, 0, 1]
3
([()()[]])