渣降临@Palindrome Partitioning II

又是经典DP,又是没做对。挫败


上来看错题了,以为是求分割方法,呼呼呼几下算法切出来,结果aaaaaaa那个超长数据集超时,这才看题。兵家大忌!看清题目!!


求最小切的次数。


我觉的算法题,千万不要一上来就陷入一个局部问题去考虑某某地方怎么优化,而应该是从全局的角度考虑问题的思路,确定复杂度,确定了复杂度,你才能明白哪里是关键,再去有针对性的优化。否则,这边还是O(N^3)着呢,你去优化O(N)的地方到了O(lgN)?蛋疼。


而且上来不要考虑最优解,很容易让你陷入死胡同,简单来说,就是上来不要怕麻烦,先来个复杂解,再想办法用记忆,用DP,用dfs bfs来优化。这才是根本。


最后来看这个题

Palindrome Partitioning II

 :


求最小分割次数,最笨的方法,先求出来可达矩阵,即任何两点之间是不是回文先判断出来。

然后就是走路了,最笨的方法就是从0开始走,用贪心尽量走得远,走到len就是一个解,最后比比最小的解就是最终结果。

毫无疑问,超时。

超时解:

char[] cc;
	int len;
	int[] count;
	int[][] isp;

	public boolean jisp(int start,int end){
		int ost=start;
		Boolean res=null;
		while(start<=end){
			if(cc[start]==cc[end]){
				if(end-start<=1){
					res=true;
					break;
				}
				if(isp[start+1][end-1]==1){
					res=true;
					break;
				}
					
			}else
				return false;
			start++;
			end--;
		}
		if(res)
			while(ost<=start){
				isp[start][end]=1;
				start--;
				end++;
			}
		return res;
	}
	public int dp(int start){
		if(count[start]!=0)
			return count[start];
		if(start>=len)
			return 0;
		int currentC=Integer.MAX_VALUE;
		for(int i=len-1;i>=start;i--)
			if(isp[start][i]==1||jisp(start,i)){
				int son=dp(i+1);
				if(currentC>son+1)
					currentC=son+1;
			}
		count[start]=currentC;
		return count[start];
	}
	public int minCut(String s) {
		cc=s.toCharArray();
		len=cc.length;
		count=new int[len+3];
		isp=new int[len][len];
		return dp(0)-1;
	}

这里面还用了点技巧,求回文的时候用一个isp来存储尝试的结果,如果一个start 到end是回文,那么,从start++ 到end-- 循环里面的都是回文。

但这个可以继续改进,分析一下复杂度,其实dp里面已经是O(N)了,因为存在记忆,所以dp(0~len)算一遍拉到,这个题的关键在哪里也就呼之欲出了:求回文的方法。


回头看一下我这个求回文,优化还不够,面对那个大数据的aaaabbaaaa有个问题就是很多时候求回文求到最后发现原来不是回文,下次还要从新判,浪费了大量时间,所以fail fast一下,果然就通过了


char[] cc;
	int len;
	int[] count;
	int[][] isp;

	public boolean jisp(int start, int end) {
		if (isp[start][end] == -1)
			return false;
		if (isp[start][end] == 1)
			return true;
		int ost = start;
		int oed = end;
		Boolean res = null;
		while (start <= end) {
			if (cc[start] == cc[end]) {
				if (end - start <= 1) {
					res = true;
					break;
				}
				if (isp[start + 1][end - 1] == 1) {
					res = true;
					break;
				}

			} else {
				res = false;
				break;
			}
			start++;
			end--;
		}
		if (res)
			while (ost <= start) {
				isp[start][end] = 1;
				start--;
				end++;
			}
		if (!res)
			while (ost <= start) {
				isp[start][end] = -1;
				start--;
				end++;
			}
		return res;
	}

	public int dp(int start) {
		if (count[start] != 0)
			return count[start];
		if (start >= len)
			return 0;
		int currentC = Integer.MAX_VALUE;
		for (int i = len - 1; i >= start; i--)
			if (isp[start][i] == 1 || jisp(start, i)) {
				int son = dp(i + 1);
				if (currentC > son + 1)
					currentC = son + 1;
			}
		count[start] = currentC;
		return count[start];
	}

	public int minCut(String s) {
		cc = s.toCharArray();
		len = cc.length;
		count = new int[len + 3];
		isp = new int[len][len];
		return dp(0) - 1;
	}


基本没怎么改,就是把false的时候,外圈也全部false了,下次判的时候就不用再从新遍历了。


但是看看神的解,还是有差距啊

public class Solution {
    public int minCut(String s) {
        int[][] dp=new int[s.length()][s.length()];
        int[] count=new int[s.length()+1];
        
        for(int i=s.length()-1;i>=0;i--)
        {
            count[i]=Integer.MAX_VALUE;
            for(int j=i;j<s.length();j++)
            {
                if(s.charAt(i)==s.charAt(j)&&(j-i<2||dp[i+1][j-1]==1))
                {
                	dp[i][j]=1;
                	count[i]=Math.min(1+count[j+1],count[i]);
                }
            }
        }
        
        return count[0]-1;       
    }
}

估计不解释下下次自己在看都看不懂了,循环中的那两行。

第一行是在判可达矩阵,第二行是在求当前的最短切割。外圈i是start,内圈j是end

但是首先要判start-end是否是回文,回文判的时候用了记忆:

1. end-start<2 直接有结果

2. >2时,只看start-1 end+1  为什么可以?因为start-1外圈i已经转过,end+1那时候肯定也判定过,所以start-1

end+1不是就不是,是就一定是

一旦判定是回文,然后记录下来(为了求下一个外圈)。

同时算count,count记录的就是最优子结构:从count【i】开始切几下就好。容易得到状态专一方程

f(i)=Math.min(f[j+1]+1)&&dp[i][j]==1;

所以那一行程序很好理解

参考链接

http://blog.youkuaiyun.com/yutianzuijin/article/details/16850031

最后推荐一下 神降临 吉木


code rewrite 

int len;
	char[] cc;
    public int minCut(String s) {
		len = s.length();
		cc = s.toCharArray();
		byte[][] dp=new byte[len+5][len+5];
		int[] minjump = new int[len + 5];
		for (int i = 0; i < minjump.length; i++)
			minjump[i] = Integer.MAX_VALUE;
		minjump[len] = 0;
		
		for (int i = len - 1; i >= 0; i--) {
			int currentMinj = Integer.MAX_VALUE;
		    for(int j=len-1;j>=i;j--){
		        if((j-i<=1||dp[i+1][j-1]==1)&&cc[i]==cc[j]){
		            dp[i][j]=1;
		            currentMinj=Math.min(currentMinj,minjump[j+1]);
		        }
		    }
			minjump[i]=currentMinj+1;
		}
		return minjump[0] - 1;
	}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值