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