滴血总结(java版):最长公共子序列(子串)、最长公共回文子序列(子串)、最长公共前缀(后缀)

1,最长公共前缀问题

有点类似冒泡算法,每次都要找最小的串的长度,然后进行截取,代码如下

public String longestCommonPrefix(String[] strs) {
         if(strs.length==0) return "";
         String s=strs[0];
          for (int i = 1; i < strs.length; i++) {
               if(s.length()==0||strs[i].length()==0) return "";
               int len=Math.min(s.length(), strs[i].length());                
    //上面错在多加了一个count,但是在实际的时候当"a""b的时候,count为0 截取的子串还是“a”
               int j;
               for (  j = 0; j <len; j++) {
                    //这里不能是==
                    if (s.charAt(j)!=strs[i].charAt(j))
                         break;
               }
               s=s.substring(0, j);//0到j-1的位置
          }
          return s;
    }


2,最长公共后缀

与上面的前缀类似,只是一些简单的坐标要变

  public static String longestCommonLastfix(String[] strs) {
             if(strs. length==0) return "";
             //s总是暂时放求的的当前的值
             String s=strs[0];
             for ( int i = 1; i < strs. length; i++) {
                      if(strs[i].length()==0||s.length()==0)
                            return "";
                      int len1=strs[i].length();
                      int len2= s.length();
                   int len=Math. min(len1,len2);
                   int j;
                   for ( j=0;j<len; j++) {
                           if(s.charAt(len2-1-j)!=strs[i].charAt(len1-1-j))
                                 break;
                     }
                     s=s.substring(len2-j);
                }           
            return s;
         }


3,最长公共子串(返回子串长度或子串)

关于子串与子序列的问题,不想赘述了,分不清的话自己百度

思路:

子串是连续的,考虑动态规划的方法进行计算
dp[i][j]表示X[0-i]与Y[0-j]的最长公共子串的长度(包含x[i],y[j]的时候)
x[i]与y[j]来说要么与之前的公共子串构成新的公共子串;或者是补构成公共子串
1,x[i]=y[j],最边上的两个char相同,dp[i][j]=dp[i-1][j-1]+1
2,x[i]!=y[j],dp[i][j]=0
最大的值就是所有dp[i][j]中的最大值
// 动态规划问题
      /*
      * dp[i][j]表示X[0-i]与Y[0-j]的最长公共子串的长度
      * x[i]与y[j]来说要么与之前的公共子串构成新的公共子串;或者是补构成公共子串
      * 1,x[i]=y[j],最边上的两个char相同, dp[i][j]=dp [i-1][j-1]+1
      * 2,x[i]!=y[j], dp[i][j]=0,因为若不相等,后面包含此串的串公共长度肯定在这后面开始算
      */

   

 public static int longestCommonSubString(String s1,String s2) {
       int len1=s1.length(),len2=s2.length();
       if(len1==0||len2==0) return 0;
        //初始化的时候数组为0,所以后面只需要判断不为0的一些情况
       int[][] dp=new int[len1][len2];
       int max=0;
       for (int i = 0; i < len1; i++) {
            for ( int j = 0; j < len2; j++) {
                 if(s1.charAt(i)==s2.charAt(j)){
                      //i或j中有一个为0,情况特殊
                      if(i==0||j==0){
                           dp[i][j]=1;
                     }
                      else if(i!=0&&j!=0)     dp[i][j]=dp[i-1][j-1]+1;
                      if(dp[i][j]>max){
                           max=dp[i][j];
                      //可以知道最大的起始索引下标为i+1-max
                     }
                }
           }
     }          
            return max;
     }
在求最大长度的时候,顺便也把最大子串的起始坐标也知道了i+1-max,所以不论是返回子串还是子串的长度都不怕怕


4,最长回文子串

最长回文子串除了之前的必须是连续的子串外,还要求是回文
最长回文子串算法除了暴力方法外,还有动态规划算法(DP)和著名的Manacher算法,最优的时间复杂度为O(N)
首先要了解一下Manacher算法,也就是在所有的串串中间加一些特殊的分隔符,这样就简单话了奇数回文和偶数回文,不懂这个算法的可以去百度下

这里先给出我自己实现的算法,看不懂的慢慢看

/*
 * str_s = a b a a b a; 
   Index = 0 1 2 3 4 5 6 7 8 9 10 11 12 
   str[] = # a # b # a # a # b #  a #; 
   P[]   = 0 0 0 2 0 1 5 1 0 3 0  1 0; 
 */
     //最长公共子串,manacher 算法
      public static String longestPalindrome(String s) {
            if(s.length()==0||s== null) return "";
            if(s.length()==1) return s;
            int len=s.length();
             char[] ch= new char[2*len+1];
             int[] p= new int [2*len+1];
             //max是坐标id影响的最远的index,不是最大距离
             int max=0;
             //id表示的是影响到最远id的index,但不一定是最大的,所以后面还要寻找
             int id=0; //对称中心
             //添加特殊字符,一般是"#"
            for ( int i = 0; i < s.length(); i++) {
                ch[2*i+1]=s.charAt(i);
                ch[2*i]= '#';
           }
          ch[2*len]= '#';
         
            //进行判断
            for ( int i = 1; i < ch. length; i++) {
             //先把能进行扩展的p[i]值找到
                 if(max>i)
                     p[i]=Math. min(p[2*id-i], max-i);
                
                 //对i点进行扩展
                 while ((1+i+p[i])<ch.length&&(i-p[i]-1)>0&&(ch[1+i+p[i]]==ch[i-p[i]-1])) {
                     p[i]++;
                }
                
                 if(p[i]+i>max){
                     max=p[i]+i;
                     id=i;
                }
           }
     
           //寻找最大值
            max=0;
            id=0;
            for ( int i = 0; i < p. length; i++) {
                 if(max<p[i]){
                     max=p[i];
                     id=i;
                }
                     
           }
            //最大长度为原来的max+1
            StringBuffer sb= new StringBuffer();
            for ( int i = id-max; i <=id+max; i++) {
                 if(ch[i]!= '#') sb.append(ch[i]);
           }
            return sb.toString();
         }


求解回文子串法二:DP,动态规划

//动态规划求最长回文子串
       /*
      * dp[i][j]表示从字符串i到j之间的字符串是否是回文串, dp[i][j]=0,i到j不是回文, dp[i][j]=1,i到j是回文
       * dp[i][j]=dp [i+1][j-1]  s[i]=s[j]
       * dp[i][j]=0 s[i]!=s[j] i-j的子串不是回文子串,不去理会,初试的时候就是0
       *
       *动态规划的时候,此时是按子串的长度进行的
       */

 /**
    * 最长回文子串
    * 有经典的线性时间的算法,记不住
    * 
    * 还是用动态规划
    * 
    * dp[i][j]表示x[i]到x[j]之间的字符串是不是回文,  0表示不是回文,1表示是回文
    * j每次从i当前位置递增到len;而len从开始递增到最大长度
    * 
    * dp[i][j]=dp[i+1][j-1]  x[i]=x[j]
    * dp[i][j]=0             x[i]!=x[j]
    *  
    *  
    *  也是有点暴力的感觉,根据长度来判断,每次都从i=0,开始,一直到长度为最大长度
    * 
    */
   public static String	longestPalindromeSubString(String s){
		if(s==null||s.length()==0) return "";
		if(s.length()==1)  return s;
		int len=s.length();
		int [][]dp=new int[len][len];
	   
		int maxLen=1,maxIndex=0;//起始的最大为1,下标为0
		
		//长度为1
		for(int i=0;i<len;i++){
			dp[i][i]=1;//x[i]到x[i]的最大回文子串
		}
		
		for(int lenl=2;lenl<=len;lenl++){//按长度递增
			
			for(int i=0;i<=len-lenl;i++){//左边的i
				int j=i+lenl-1;
				if(s.charAt(i)==s.charAt(j)){
					dp[i][j]=1;
					maxIndex=i;
					maxLen=lenl;
				}
			}
			
		}
	   
	   return s.substring(maxIndex,maxIndex+maxLen);
	}





5,最长公共子序列

  1.    先来一个返回子序列长度的版本  
/DP问题,动态规划一下 dp[i][j]表示x长度为i,y长度为j时候的最长公共子序列的长度
/*
 * 初始时候 dp[i][j]=0,
 * dp[i][j]=0,如果i或者j为0
 * dp[i][j]=dp[i-1][j-1]+1,如果前面的x[i-1]=y[j-1]
 * dp[i][j]=max{dp[i-1][j], dp[i][j-1]},当x[i-1]=y[j-1]
 * 这有点类似 leetcode最大抢劫问题,最大的就是 dp[len -1][len-1]的最大,但是这个是从长度为0开始,不是从下标为0    开始,
 */
    
public static int LCSubseq(String s1,String s2){
            if(s1.length()==0||s2.length()==0) return 0;
            int len1=s1.length();
            int len2=s2.length();
            int[][] dp= new int[len1+1][len2+1];
                     
            for ( int i = 1; i <=len1; i++) {
                 for ( int j = 1; j <=len2; j++) {
                
                   if(s1.charAt(i-1)==s2.charAt(j-1))
                        dp[i][j]=dp[i-1][j-1]+1;
                   else{
                       dp[i][j]=Math.max(dp[i-1][j], dp[i][j-1]);                
                   }   
                }
           }
            return dp[len1][len2];
     }
  2,返回一个子序列版本的,代码大致差不多
 
public static String LCSubseq(String s1,String s2){
        if(s1.length()==0||s2.length()==0) return "";
        int len1=s1.length();
        int len2=s2.length();
        int[][] dp= new int[len1+1][len2+1];
                 
        for ( int i = 1; i <=len1; i++) {
             for ( int j = 1; j <=len2; j++) {
            
               if(s1.charAt(i-1)==s2.charAt(j-1))
                    dp[i][j]=dp[i-1][j-1]+1;
               else{
            	   dp[i][j]=Math.max(dp[i-1][j], dp[i][j-1]);        
               }   
            }
       }
        int max= dp[len1][len2];
        System.out.println("最大长度为"+max);
        StringBuilder sb=new StringBuilder();
        //输出这个序列
        while(len1>=1&&len2>=1){
        	if(s1.charAt(len1-1)==s2.charAt(len2-1)){
        		sb.append(s1.charAt(len1-1));
        		len1--;
        		len2--;
        		
        	}else{//不等的时候
        		if(dp[len1-1][len2]>dp[len1][len2-1])
        		len1--;//谁大就回退谁
        		else
        			len2--;	
        	}
        }
        return sb.reverse().toString();
 }

很明显,这里的动态规划是根据序列长度来规划的,下面来一个根据坐标动态规划的
<pre name="code" class="java">//最长公共子序列,输出长度就行
		public static int LCSubseq1(String s1,String s2){
			if(s1.length()==0||s2.length()==0) return 0;
			int len1=s1.length();
			int len2=s2.length();
			int[][] dp=new int[len1][len2];
			StringBuffer sb=new StringBuffer();	
				
			//初始化
			for (int i = 0; i < len1; i++) {
				if(s1.charAt(i)==s2.charAt(0))
					dp[i][0]=1;
			}
			//初始化
				for (int i = 0; i < len2; i++) {
						if(s1.charAt(0)==s2.charAt(i))
							dp[0][i]=1;
			}
				
			for (int i = 1; i <len1; i++) {
				for (int j = 1; j <len2; j++) {
				
				   if(s1.charAt(i)==s2.charAt(j))
					   {dp[i][j]=dp[i-1][j-1]+1;
				     }
				   else{
					  dp[i][j]=Math.max(dp[i-1][j], dp[i][j-1]);			   
				   }	   
				}
			}
			int max=dp[len1-1][len2-1];
			System.out.println("最大长度为:"+max);
			/*//随便输出一个长度,有点bug,输不出String,以后再来改
			int i=len1-1,j=len2-1;
			while(i>=1&&j>=1){
				if(s1.charAt(i)==s2.charAt(j)&&dp[i][j]==dp[i-1][j-1]+1){
					sb.append(s1.charAt(i));
					i--;
					j--;
				}else if(s1.charAt(i)!=s2.charAt(j)&&dp[i-1][j]>=dp[i][j-1]){
					i--;
				}else{
					j--;
				}
			}
		//有点Bug,	
			if(i==0&&j==0){
				if(s1.charAt(i)==s2.charAt(0))
					sb.append(s1.charAt(0));
			}else if(i>j){
				while(i>=0)
					if(s1.charAt(i--)==s2.charAt(0))
						sb.append(s2.charAt(0));
			}else if(i<j){
				while(j>=0)
					if(s1.charAt(0)==s2.charAt(j--))
						sb.append(s1.charAt(0));
			}
			return sb.reverse().toString();*/
			return max;
		}


 
  
6,最长回文序列
1,递归的方式求解,返回子序列
  //最长回文子序列
             //递归
       /*
      * dp[i][j]表示坐标i到j之间的最长回文长度
      * 当x[i]=x[j], dp[i][j]=dp [i+1][j-1]+2;
      * 当x[i]!=x[j], dp[i][j]=max{dp [i+1][j],dp[i][j-1]}
      *
      * 初试的时候 dp[i][j]=dp [0][len-1]
      */  
 public static String LonPaliSubse(String s){
                int maxLength= Lps(s, 0, s.length()-1);
                   System. out.println( "最大回文子序列的长度为:" +maxLength);
                   //寻找最长子序列,并输出
                  
                   return null;
              }
  public static int Lps(String s, int begin, int last){
      if(begin==last)
            return 1;
      if(begin>last) return 0;
      if(s.charAt(begin)==s.charAt(last))
            return Lps(s, begin+1, last-1)+2;
      return Math. max(Lps(s, begin, last-1), Lps(s, begin+1,  last));
  }

2,动态规划,返回最大的回文序列长度
//DP规划
   /*
   * 此时的动态规划是从序列长度为1逐次递增进行动态的
   * dp[i][j] 从位置i到j之间的序列
   */
 
public static int LonPaliSubse1(String s){
       if(s.length()==0||s== null) return 0;
       int len=s.length();
       int[][] dp= new int[len][len];
       int max=0;
       //序列长度为1
       for ( int i = 0; i <len; i++)  dp[i][i]=1;
      
       //长度大于等于i+1的序列 dp[j][j+i]
       for ( int i = 1; i < len; i++) {
            for ( int j = 0; j+i<len; j++) {
                   //如果首位相同
                 if(s.charAt(j)==s.charAt(j+i))
                     max=dp[j+1][j+i-1]+2;
                 else max=Math. max(dp[j+1][j+i], dp[j][j+i-1]);
                
                dp[j][j+i]=max;
           }
     }
       return dp[0][len-1];
  }

3,构造将原字符串逆转,这样求回文子串,就是求这两个字符串的最大公共子序列,这根据上面求公共序列的情况就可以求了
public static String LonPaliSubse2(String s){
       if(s.length()==0||s== null) return "";
       StringBuffer sb= new StringBuffer(s);
       String s1=sb.reverse().toString(); 
       return LCSubseq2(s1, s);
  }
后面还有需要改进的和优化的,先到这把







  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值