Java實現算法之Brackets Sequence問題

Problem:

Description

Let us define a regular brackets sequence in the following way:

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

The input file contains at most 100 brackets (characters '(', ')', '[' and ']') that are situated on a single line without any other characters among them.

Output

Write to the output file a single line that contains some regular brackets sequence that has the minimal possible length and contains the given sequence as a subsequence.

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
([()()[]])



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章