dp编程题

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/reed1991/article/details/69218786
1.给定两个字符串AB,同时给定两串的长度nm

测试样例:"1AB2345CD",9,"12345EF",7      返回:4

public class LongestSubstring {
	public int findLongest(String A, int n, String B, int m) {
      int max=0; 
	int[][]	dp=new int[n][m];
	for(int i=0;i<n;i++)
		for(int j=0;j<m;j++)
		{
			dp[i][j]=0;
		}
	for(int i=0;i<n;i++)
		for(int j=0;j<m;j++)
		{
			if(A.charAt(i)==B.charAt(j))
			{
				if(i==0||j==0)
					dp[i][j]=1;
				else
					dp[i][j]=dp[i-1][j-1]+1;
				max=dp[i][j]>max?dp[i][j]:max;
			}			
		}	
	return max;	
    }
}



/*题目要求公共子串的元素必须相邻, dp矩阵是按照这样的思路想出来的:
首先:
用一个矩阵来记录两个字符串中所有位置的两个字符之间的匹配情况,若是匹配则为1,否则为0。
然后求出对角线最长的1序列,其对应的位置就是最长匹配子串的位置.

下面是字符串21232523311324和字符串312123223445的匹配矩阵,前者为X方向的
,后者为Y方向的。不难找到,红色部分是最长的匹配子串。通过查找位置我们得到最长的匹配子串为:21232

   0 0 0 1 0 0 0 1 1 0 0 1 0 0 0
   0 1 0 0 0 0 0 0 0 1 1 0 0 0 0
   1 0 1 0 1 0 1 0 0 0 0 0 1 0 0
   0 1 0 0 0 0 0 0 0 1 1 0 0 0 0
   1 0 1 0 1 0 1 0 0 0 0 0 1 0 0
   0 0 0 1 0 0 0 1 1 0 0 1 0 0 0
   1 0 1 0 1 0 1 0 0 0 0 0 1 0 0
   1 0 1 0 1 0 1 0 0 0 0 0 1 0 0
   0 0 0 1 0 0 0 1 1 0 0 1 0 0 0
   0 0 0 0 0 0 0 0 0 0 0 0 0 1 0
   0 0 0 0 0 0 0 0 0 0 0 0 0 1 0
   0 0 0 0 0 1 0 0 0 0 0 0 0 0 0
   0 0 0 0 0 0 0 0 0 0 0 0 0 0 0


但是在0和1的矩阵中找最长的1对角线序列又要花去一定的时间。
通过改进矩阵的生成方式和设置标记变量,可以省去这部分时间。
优化:

   0 0 0 1 0 0 0 1 1 0 0 1 0 0 0
   0 1 0 0 0 0 0 0 0 2 1 0 0 0 0
   1 0 2 0 1 0 1 0 0 0 0 0 1 0 0
   0 2 0 0 0 0 0 0 0 1 1 0 0 0 0
   1 0 3 0 1 0 1 0 0 0 0 0 1 0 0
   0 0 0 4 0 0 0 2 1 0 0 1 0 0 0
   1 0 1 0 5 0 1 0 0 0 0 0 2 0 0
   1 0 1 0 1 0 1 0 0 0 0 0 1 0 0
   0 0 0 2 0 0 0 2 1 0 0 1 0 0 0
   0 0 0 0 0 0 0 0 0 0 0 0 0 1 0
   0 0 0 0 0 0 0 0 0 0 0 0 0 1 0
   0 0 0 0 0 1 0 0 0 0 0 0 0 0 0
   0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
   

          当字符匹配的时候,我们并不是简单的给相应元素赋上1,而是赋上其左上角元素的值加一。
          我们用两个标记变量来标记矩阵中值最大的元素的位置,在矩阵生成的过程中来判断当前生成的元素的值是不是最大的,
          据此来改变标记变量的值,那么到矩阵完成的时候,最长匹配子串的位置和长度就已经出来了。*/

 ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

0-n之间的数,少了其中一个,怎么找到少的那个

可以用异或

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

最长回文字串问题

对于一个字符串,请设计一个高效算法,计算其中最长回文子串的长度。

给定字符串A以及它的长度n,请返回最长回文子串的长度。
测试样例:

"abc1234321ab",12

返回:7

public class Palindrome {
    public int getLongestPalindrome(String A, int n) {
        // write code here
        int[][] dp = new int[n][n];
        int max = 1;
        for (int i = 0; i < n; ++i) {
            dp[i][i] = 1;
        }
        char[] a = A.toCharArray();
        for (int len = 2; len <= n; ++len) {
            for (int i = 0; i <= n - len; ++i) {
                int j = i + len - 1;
                if (len == 2 && a[i] == a[j]) {
                    dp[i][j] = len;
                    max = 2;
                    continue;
                }
                if (a[i] == a[j] && dp[i + 1][j - 1] != 0) {
                    dp[i][j] = len;
                    max = len;
                }
            }
        }
        return max;
    }
}

动态规划
1.dp[i][j]表示,若i到j已经是回文串了,那么dp[i][j]是回文串的长度,否则为0;
2.初始时dp[i][i]=1,i=0,2,3...n-1;
3.递归公式,len>2时,当dp[i+1][j-1]!=0,且a[i]==a[j]时,dp[i][j] = j-i+1+dp[i+1][j-1],  否则dp[i][j]=0,i<j。这是一个从已知回文串往两边展开的过程。

当len=2时,特殊处理一下,因为当len=2时,dp[i+1][j-1]会访问到dp矩阵的左下方,我们没对那个位置做初始化处理,因此不能直接用递推公式

--------------------------------------------------------------------------------------------------------------------------------------------------------------

对于一个数字序列,请设计一个复杂度为O(nlogn)的算法,返回该序列的最长上升子序列的长度,

给定一个数字序列A及序列的长度n,请返回最长上升子序列的长度。

测试样例:

[2,1,4,3,1,5,6],7

public class AscentSequence {
	public static void main(String[] args) {
		int[] test=new int[]{2,1,4,3,1,5,6};
		int a=7;
		AscentSequence as=new AscentSequence();
		System.out.println(as.findLongest(test,a));
	}
	public  int findLongest(int[] A, int n) {
      int length=A.length,end=0;
      int[] B=new int[length];
		B[0]=A[0];
		for(int i=1;i<length;i++)
		{
		if(A[i]>B[end])
		{
			B[++end]=A[i];
		}
		else
		{
			int pos=findpos(B,A[i],0,end);
			B[pos]=A[i];
		}	
		}	
	return end+1;	
    }
	private int findpos(int[] B, int n, int start, int end) {
        while (start < end) {
            int mid = start + (end - start) / 2;
          /* 直接使用(high + low) / 2 可能导致溢出
     int型数据,java里除号是下取整的,二分法,你mid有不断增加的可能,加法就容易溢出,
     超过int型数据的表达范围,比如计算2个32位的数字,加法结果为64位,但是制定了数据类型为32位,
     结果就只能读这个64位数的低32位,这就是溢出。

            有可能二分查找到数组的最后两个,如果数组的长度非常大,就有可能溢出

            如果你要在数量为十亿级别数据库中查找,就要考虑这个溢出。*/
            if (B[mid] < n) {
                start = mid + 1;
            } else if (B[mid] > n) {
                end = mid ;
            } else {
                return mid;
            }
        }
        return start;
    }
}

/*下面一步一步试着找出它。

我们定义一个序列B,然后令i = 08逐个考察这个序列。

此外,我们用一个变量end来记录B中最后一个数的下标。

 

首先,令B[0] = A[0] = 2,就是说当只有1一个数字2的时候,长度为1LIS的最小末尾是2,这时end=0

 

然后,把A[2]有序地放到B里,令B[0] = 1,就是说长度为1LIS的最小末尾是1B[0]=2已经被淘汰了,这时end =1

 

接着,A[2] = 5A[2]>B[0],所以令B[1]=A[2]=5,就是说长度为2LIS的最小末尾是5,很容易理解吧。这时候B[0..1] = 1, 5end1

 

再来,A[3] = 3,它正好加在1,5之间,放在1的位置显然不合适,因为1小于3,长度为1LIS最小末尾应该是1,这样很容易推知,

长度为2LIS最小末尾是3,于是可以把5淘汰掉,这时候B[0..1] = 1, 3end = 2

 

继续,A[4] = 6,比B中最大的数3还要大 ,于是很容易可以推知B[2] = 6, 这时B[0..2] = 1, 3, 6end = 2

 

A[5] = 4,它在36之间,于是我们就要把6替换掉,得到B[2] = 4B[0..2] = 1, 3, 4end继续等于2

 

A[6] = 8,它很大,比4大。于是继续往B中追加,B[3] = 8end变成3了。

 

A[7] = 9,得到B[4] = 9,此时B1,3,4,8,9end4

 

最后一个, A[8] = 7,它在48之间,所以我们知道,最新的B[3] =7B[0..4] = 1, 3, 4, 7, 9end = 4

 

于是我们知道了LIS的长度为end+1 = 5

 

注意: 这个1,3,4,7,9不是LIS字符串,比如本题例的LIS应该是1,3,4,8,97代表的意思是存储4位长度LIS的最小末尾是7

所以我们的B数组,是存储对应长度LIS的最小末尾。有了这个末尾,我们就可以一个一个地插入数据。

虽然最后一个A[8] = 7更新进去对于这组数据没有什么意义,但是如果后面再出现两个数字89

那么就可以把8更新到B[4], 9更新到B[5],得出LIS的长度为6


然后应该发现一件事情了:在B中插入数据是有序的,而且是进行替换而不需要挪动——也就是说,我们可以使用 二分查找 ,

将每一个数字的插入时间优化到 O(logN),于是算法的时间复杂度就降低到了O(NlogN)*/

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

求连续子数组的最大和

public class MaxNumber {
public static void main(String[] args) {
	int A[]=new int[]{2,1,-4,3,-1,5,6};
	System.out.println(biggest(A,7));
}
public static  int biggest(int[] A,int length){
	int MaxSum=0;
	int CurSum=0;
	for(int i=0;i<length;i++){
		CurSum+=A[i];
		if(CurSum>MaxSum){
			MaxSum=CurSum;	
		}
		if(CurSum<0)
			CurSum=0;
	}	
	return MaxSum;
}
}


------------------------------------------------------------------------------------------------------------------------------------------------

笔试面试算法经典--全排列算法-递归&字典序实现(Java)

全排列算法-递归&字典序实现

全排列:
从n个不同元素中任取m(m≤n)个元素,按照一定的顺序排列起来,叫做从n个不同元素中取出m个元素的一个排列。当m=n时所有的排列情况叫全排列。
例如:

1 、2 、3三个元素的全排列为:

{1,2,3},{1,3,2},{2,1,3},{2,3,1},{3,1,2},{3,2,1}。

解法1(递归)
如下图:要对1、2、3、4进行排序,第一个位置上的元素有四种可能:1或2或3或4,假如已经确定了第一个元素为4,剩下的第二个位置上可以是1、2、3,很显然这具有递归结构,如果原始要排列的数组顺序为1、2、3、4,现在只要分别交换1、2,1、3,1、4然后对剩下的3个元素进行递归的排列。

public  void Permutation(char chs[],int start )
    {
        if(start==chs.length-1)
        {
            Arrays.toString(chs);
            //如果已经到了数组的最后一个元素,前面的元素已经排好,输出。
        }
        for(int i=start;i<=chs.length-1;i++)
        {
        //把第一个元素分别与后面的元素进行交换,递归的调用其子数组进行排序
                Swap(chs,i,start);
                Permutation(chs,start+1);
                Swap(chs,i,start);
        //子数组排序返回后要将第一个元素交换回来。  
        //如果不交换回来会出错,比如说第一次1、2交换,第一个位置为2,子数组排序返回后如果不将1、2
        //交换回来第二次交换的时候就会将2、3交换,因此必须将1、2交换使1还是在第一个位置 
        }
    }
    public  void Swap(char chs[],int i,int j)
    {
        char temp;
        temp=chs[i];
        chs[i]=chs[j];
        chs[j]=temp;
    }

递归方法会对重复元素进行交换比如使用递归对{1,1}进行全排序会输出:{1,1},{1,1}两个重复的结果。要在排序的时候去掉重复结果,可以修改一下代码如下:

public static void Permutation(char chs[],int start)
    {
        if(start==end)
        {
            list.add(new String(chs));
        }
        for(int i=start;i<=chs.length-1;i++)
        {
            if(i==start||chs[i]!=chs[start])
            {
            //在排列的时候进行判断如果后面的元素与start相同时就不进行排序。
            //这样就可以避免对重复元素进行排序
                Swap(chs,i,start);
                Permutation(chs,start+1);
                Swap(chs,i,start);
            }
        }
    }



解法2(字典序法)

字典序法
对给定的字符集中的字符规定了一个先后关系,在此基础上规定两个全排列的先后是从左到右逐个比较对应的字符的先后。
列如:对a、b、c进行排序的结果是{a,b,c}、{a,c,b}、{b,a,c}、{b,c,a}、{c,a,b}、{c,b,a}
字典序法的优点是排列的结果按照顺序输出并且对于重复的元素不进行重复排序。

字典排序法的思想:

例如:对元素1,2,3,4进行排序,假设默认的数组顺序为{1,2,3,4},先输出第一个排列:1、2、3、4。然后从右向左找到第一个非递增的数,4,3,因为3比4小,交换3、4,并且对3后面的数进行逆序排列,第二个排列为{1,2,4,3},再从右向左3,4,2,发现2比4小,交换从右向左第一个比2大的数,交换后{1,3,4,2}再对3后面的数进行逆序排列第三个序列为:{1,3,2,4}
依次循环直到数组成为完全递减数组结束1、2、3、4字典排序的最大序列为{4,3,2,1}。

public  void PermutationWithDictionary(char chs[])
    {
        Arrays.sort(chs);
        //先对数组的元素进行依次排序
        while(true)
        {
            System.out.println(chs);
            int j=chs.length-1;
            int index=0;
            for(j=chs.length-2;j>=0;j--)
            {
                if(chs[j]<chs[j+1])
                {
                    index=j;
                    break;
                    //从右向左找到第一个非递增的元素
                }
                else if(j==0){
                    return;
                }
            }           

            for(j=chs.length-1;j>=0;j--)
            {
                if(chs[j]>chs[index])
                    break;
                    //从右向左找到第一个比非递增元素大的元素
            }
                Swap(chs,index,j);
                //交换找到的两个元素
                Reverse(chs,index+1);
                //对非递增元素位置后面的数组进行逆序排列
        }       
    }
    public static void Reverse(char chs[],int i)
    {
        int k=i,j=chs.length-1;
        while(k<j)
        {
            Swap(chs,k,j);
            k++;
            j--;
        }
    }

    public static void Swap(char chs[],int i,int j)
    {
        char temp;
        temp=chs[i];
        chs[i]=chs[j];
        chs[j]=temp;
    }



阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页