zoukankan      html  css  js  c++  java
  • 最长回文子串

    问题描述:

    给定一个字符串S,找出它的最长回文子串并返回,最长子串的长度不超过1000.

    Example:

    Input: "babad"
    
    Output: "bab"
    
    Note: "aba" is also a valid answer.
    

    Example:

    Input: "cbbd"
    
    Output: "bb"
    

     方法1(自己想的)

     把子串分成两类,奇子串和偶子串,再分别以这一个字母和两个字母为中心进行扩展,时间复杂度为O(n*2)。

     public String longestPalindrome(String s) {
            int maxLen = 0;
            int a=0,b=0,x=0,y=0;//a和b存放当前子串的下标,x和y存放当前最长回文子串的下标。
    
            for(int i=0;i<s.length();i++) {
                int len =1;
                
                a=i;
                b=i;
                for(int j=1;j<=s.length()/2;j++) {//循环扩展i,并把i当做是奇回文                    
                    if((i-j)>=0 && (i+j)<s.length()) {//判断是否超出了边界
                        if (s.charAt(i-j)==s.charAt(i+j)) {//如果i和i+1相等,则相应增减len和a,b
                            len +=2;
                            a--;
                            b++;
                        }
                        else break;
                    }
                }
                
                if(len>maxLen) {//判断一次最大回文长度
                    x = a;
                    y = b;
                    maxLen = len;
                }
                
                if(i<s.length()-1 && s.charAt(i)==s.charAt(i+1)) {//这里的i和i+1的字母是相等的,把他两当做是偶回文的中心
                    len =2;//len和a,b做相应的改变
                    a = i;
                    b = i+1;
                    for(int j=1;j<=s.length()/2;j++) {//扩展i和i+1
                        if((i-j)>=0 && (i+j+1)<s.length()) {
                            if (s.charAt(i-j)==s.charAt(i+j+1)) {
                                len +=2;
                                a--;
                                b++;
                            }
                            else break;
                        }
                    }
                }            
                
                if(len>maxLen) {//判断最大回文长度
                    x = a;
                    y = b;
                    maxLen = len;
                }
            }
            
            return s.substring(x,y+1);//输出最长回文子串
        }

    方法2(整理中)

     首先,在字符串s中,用rad[i]表示第i个字符的回文半径,即rad[i]尽可能大,且满足:
    s[i-rad[i],i-1]=s[i+1,i+rad[i]]
    很明显,求出了所有的rad,就求出了所有的长度为奇数的回文子串.
    至于偶数的怎么求,最后再讲.
    假设现在求出了rad[1..i-1],现在要求后面的rad值,并且通过前面的操作,得知了当前字符i的rad值至少为j.现在通过试图扩大j来扫描,求出了rad[i].再假设现在有个指针k,从1循环到rad[i],试图通过某些手段来求出[i+1,i+rad[i]]的rad值.
    根据定义,黑色的部分是一个回文子串,两段红色的区间全等.
    因为之前已经求出了rad[i-k],所以直接用它.有3种情况:
    浅谈manacher算法
    ①rad[i]-k<rad[i-k]
    如图,rad[i-k]的范围为青色.因为黑色的部分是回文的,且青色的部分超过了黑色的部分,所以rad[i+k]肯定至少为rad[i]-k,即橙色的部分.那橙色以外的部分就不是了吗?这是肯定的.因为如果橙色以外的部分也是回文的,那么根据青色和红色部分的关系,可以证明黑色部分再往外延伸一点也是一个回文子串,这肯定不可能,因此rad[i+k]=rad[i]-k.为了方便下文,这里的rad[i+k]=rad[i]-k=min(rad[i]-k,rad[i-k]).
    浅谈manacher算法
    ②rad[i]-k>rad[i-k]
    如图,rad[i-k]的范围为青色.因为黑色的部分是回文的,且青色的部分在黑色的部分里面,根据定义,很容易得出:rad[i+k]=rad[i-k].为了方便下文,这里的rad[i+k]=rad[i-k]=min(rad[i]-k,rad[i-k]).

    根据上面两种情况,可以得出结论:当rad[i]-k!=rad[i-k]的时候,rad[i+k]=min(rad[i]-k,rad[i-k]).
    注意:当rad[i]-k==rad[i-k]的时候,就不同了,这是第三种情况:
    浅谈manacher算法
    如图,通过和第一种情况对比之后会发现,因为青色的部分没有超出黑色的部分,所以即使橙色的部分全等,也无法像第一种情况一样引出矛盾,因此橙色的部分是有可能全等的,但是,根据已知的信息,我们不知道橙色的部分是多长,因此就把i指针移到i+k的位置,j=rad[i-k](因为它的rad值至少为rad[i-k]),等下次循环的时候再做了.
    整个算法就这样.
    至于时间复杂度为什么是O(n),我已经证明了,但很难说清楚.所以自己体会吧.

      	public static void main(String[] args) {
    		// TODO Auto-generated method stub
    		String s ="acbcdef";
    		char[] c = new char[s.length()*2+1];
    		int[] rad = new int[c.length];
    		rad[0] = 0;
    		rad[1] = 1;
    		int maxLength=0;
    		
    		for(int i=0;i<s.length();i++) {//在字符串插入#字符
    			c[2*i] = '#';
    			c[2*i+1] = s.charAt(i);
    		}
    		c[s.length()*2] = '#';
    		
    		for(int i=1,j=0,k=0;i<c.length;) {
    			while(i+j+1<c.length && i>=j+1 && c[i-j-1]==c[i+j+1]) j++;//扩展i的两边,找到其最大子串
    			
    			rad[i] = j;//将最大子串的值赋给rad
    			for(k=1;i>=k && i+k<c.length && rad[i-k]!=rad[i]-k;k++) rad[i+k] = Math.min(rad[i-k],rad[i]-k);//如果i-k与rad[i]-k的值不等,那么把最小的值赋给radi+k
    			
    			i+=k;//如果i-k与radi-k的值相等,那么进入下一次循环
    			j -= k;//下一个i已经能确定的最小半径
    		}
    		
    		for(int i=0;i<c.length;i++) maxLength = Math.max(maxLength, rad[i]);
    		
    		System.out.println(maxLength);
    		
    	}
    

      

      

  • 相关阅读:
    【leetcode】1020. Partition Array Into Three Parts With Equal Sum
    【leetcode】572. Subtree of Another Tree
    【leetcode】123. Best Time to Buy and Sell Stock III
    【leetcode】309. Best Time to Buy and Sell Stock with Cooldown
    【leetcode】714. Best Time to Buy and Sell Stock with Transaction Fee
    【leetcode】467. Unique Substrings in Wraparound String
    【leetcode】823. Binary Trees With Factors
    【leetcode】143. Reorder List
    【leetcode】1014. Capacity To Ship Packages Within D Days
    【leetcode】1013. Pairs of Songs With Total Durations Divisible by 60
  • 原文地址:https://www.cnblogs.com/whig/p/8359631.html
Copyright © 2011-2022 走看看