Java实现算法之Brackets Sequence问题

本文探讨了使用Java解决Brackets Sequence问题的动态规划方法,包括问题描述、输入输出、思路分析及代码实现。在分析过程中,作者发现对于某些特定情况,动态规划可能不是最佳解决方案,提出了可能更适合使用栈来处理该问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值