Generate Parentheses 【Leetcode】

哥们存在的意义就是为了衬托神的存在,又一次体现了渣代码和神代码的区别


又是无聊记忆搜索dp,懒得写优化的key直接拿中间结果存key了。

其实我觉得这个题的关键在于如何抽象这个结果的表示形式,我只想到了用一个二维数组进行存储,

2,1

2,1 


表示第一位置上两个左括号两个右括号,第二个位置上一个左一个右。[[]][]

还有一种特殊情况,就是

2,1

1,2

对应于[[][]]  注意dp内循环的顺序,右括号永远在左括号右边,防止出现

1,2

2,1

对应[]][[],显然这是个错误解


int[][] ms = new int[2][];
	ArrayList<String> res = new ArrayList<String>();
	HashMap<String, Integer> traceMap = new HashMap<String, Integer>();

	public String getLR(int num, int lr) {
		String s = "";
		String basic = "(";
		if (lr == 1)
			basic = ")";
		for (int i = 0; i < num; i++)
			s += basic;
		return s;
	}

	public String getRes(boolean save) {
//		String ds="";
//		for (int i = 0; i < ms[0].length; i++) {
//			if(ms[0][i]==0)
//				continue;
//			ds+=ms[0][i]+"/"+ms[1][i]+", ";
//		}
//		System.out.println(ds);
		String s = "";
		for (int i = 0; i < ms[0].length; i++) {
			if(ms[0][i]==0)
				continue;
			s += getLR(ms[0][i], 0) + getLR(ms[1][i], 1);
		}
		if (save)
			res.add(s);
		return s;
	}

	public void dp(int n, int currentMax) {
		//System.out.println("dp  n="+n+"  currentMax="+currentMax);
		String key = getRes(false);
		Integer v = traceMap.get(key);
		if (v != null)
			return;
		traceMap.put(key, 1);
		if (n <= 0){
			getRes(true);
			return;
		}
		for (int i = 0; i <currentMax; i++) {
			ms[0][i]++;
			for (int j = i; j <currentMax; j++) {
				ms[1][j]++;
				dp(n - 1, currentMax);
				ms[1][j]--;
			}
			ms[0][i]--;
		}
		ms[0][currentMax] = 1;
		ms[1][currentMax] = 1;
		dp(n - 1, currentMax + 1);
		ms[0][currentMax] = 0;
		ms[1][currentMax] = 0;
	}

	public ArrayList<String> generateParenthesis(int n) {
		ms[0] = new int[n + 1];
		ms[1] = new int[n + 1];
		dp(n, 0);
		return res;
	}

========================================

再看看人家的神代码

	ArrayList<String> res=new ArrayList<String>();
	int n;
	public void dp(String s, int l,int r){
		if(l==n){
//			for(int i=0;i<l-r;i++)
//				s+=")";
			res.add(s);
			return;
		}
		dp(s+"(",l+1,r);
		if(l>r)
			dp(s+")",l,r+1);
	}
	public ArrayList<String> generateParenthesis(int N) {
		n=N;
		dp("",0,0);
		return res;
	}

中间两句我注释掉了,不注释掉就是最终结果。这个思路太秒了。其实还是dp,但是人家分析获得的状态专一方程是和渣不一样的。 去掉注释运行一下看看结果就能想明白为什么这么做了。对于N,一定有N个左括号和N个有括号(废话)。


1.既然一定有n个左括号,我们就先把n个左括号画出来,一个左括号占一个数组位置,剩下的问题就是考虑每个数组单元里放几个右括号的问题了。(假设数组存在为a[]=new a[n]);

2.问题是随意排列,有错误解,因此需要定义错误解。从左往右读,遇到l则+1,遇到r则-1,sum<0则是错误解,从右往左亦然。所以半神解法就是来个全排列然后根据错误解定义删掉错误解。

注意:这个解法需要数组a[],这个方法如果求n个括号的解的个数的时候会很快,但在这个题里搞不过神DP啊


3. 最后在来看神的DP解,其实他用的就是最普通的状态转移方程,每添加一个字符就进入新的状态。正确性判断就是任何状态从左向右l>=r即可,注意L的控制由n完成。


个人感受:

感觉做DP就是多做多做突然就顿悟了,虽然人家都说边界条件,状态控制很多很多,但你自己没有真正遇到过就没有什么感受。

还有DP的关键就是分析,这个题解法很多,从我最笨的二维数组定义状态到神的单字符状态,重要的是结合题意,

1. 分析清楚,变化的是什么,是一个字符,一个单词,还是什么。找好变化量,往往能使半功倍

2. 搞清楚状态控制条件,如果变化量选的不合适,状态控制条件就会非常复杂

3. 记忆优化,渣都知道的东西就不提了

4. 没事了总想试试bottom up 从来没成过,这个题貌似也高不了吧


Reright @ 2014-1-31

 ArrayList<String> reslist=new ArrayList<String>();
	    int[] res;
	    int num;
	    public void genRes(){
//	    	for(int i:res)
//	    		System.out.print(i+" ");
//	    	System.out.println();
	        String s="";
	        for(int i=1;i<res.length;i++){
	            s+="(";
	            for(int j=0;j<res[i];j++)
	                s+=")";
	        }
	        reslist.add(s);
	    }
	    public void dp(int position,int usedright){
	    //	System.out.println("dp  "+position+"  "+usedright);
	        if(usedright>=position)
	            return;
	        if(position==num){
	            res[num]=num-usedright;
	            genRes();
	            return;
	        }
	        for(int i=0;i<=position;i++){
	            res[position]=i;
	            dp(position+1,usedright+i);
	        }
	    }
	    public ArrayList<String> generateParenthesis(int n) {
	        res=new int[n+1];
	        if(n==0)
	            return reslist;
	        num=n;
	        dp(1,0);
	        return reslist;
	    }



Code Rewrite:



 ArrayList<String> reslist = new ArrayList<String>();
	int num;

	public void dp(int p, int leftp, String s) {
		if (p == num){
			for(int i=1;i<=leftp;i++)
				s+=")";
			reslist.add(s);
			return;
		}
		String ts = "";
		for (int i = 0; i <= leftp; i++) {
			dp(p + 1, leftp + 1 - i, s + ts+ "(" );
			ts += ")";
		}
	}

	public ArrayList<String> generateParenthesis(int n) {
		num = n;
		dp(0, 0, "");
		return reslist;
	}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值